Scala:带有List和Option的函数中的无名参数

时间:2019-05-22 06:53:26

标签: scala function scala-cats

2个不同的示例,第一个有效:

  import cats.syntax.either._
  val e = 10.asRight[String]
  def i2s(i:Int):String = i.toString
  e.map(i => List(i2s(i))) //using explicit parameter
  e.map(List(i2s(_)))   //using no-name _ parameter

现在没有编译与Option相同的示例:

e.map(Option(i2s(_))) 

错误:

Error:(27, 15) type mismatch;
 found   : Option[Int => String]
 required: Int => ?
  e.map(Option(i2s(_)))

使用显式参数可以正常工作:

e.map(i => Option(i2s(i)))

在两种情况下,都使用List和Option调用apply方法。 List.apply签名:

 def apply[A](xs: A*): List[A] = ???

Option.apply签名:

 def apply[A](x: A): Option[A]

请说明区别。

2 个答案:

答案 0 :(得分:4)

您的两个List示例都可以编译,但是它们并不意味着同一件事,也不会产生相同的结果。

e.map(i => List(i2s(i))) //res0: scala.util.Either[String,List[String]] = Right(List(10))
e.map(List(i2s(_)))      //java.lang.IndexOutOfBoundsException: 10

第一个很容易理解,那么第二个到底发生了什么?

正在发生的事情是您正在使用eta expansion通过Int => String方法创建一个i2s()函数。然后,您使用该单个函数作为列表中的唯一元素填充List,然后尝试检索索引10上的值(该索引不存在,因此是异常)。

如果将第一行更改为val e = 0.asRight[String],则该异常消失了,因为在索引0上存在了一些 ,是刚刚放在其中的函数。

>

之所以进行编译,是因为List实例将接受Int作为参数(通过隐藏的apply()方法),但是接受Option instance 没有采用apply()(*)的Int方法,因此无法编译。

(*)Option对象确实具有apply()方法,但这是另一种动物。

答案 1 :(得分:2)

关于您使用List[A]的第一个示例起作用的原因,这里有很多事情在起作用。首先,让我们看一下表达式上发生的扩展:

val res: Either[String, Int => String] = 
  e.map[Int => String](List.apply[Int => String](((x$1: Int) => FlinkTest.this.i2s(x$1))));

注意两件事:

  • lambda表达式的扩展发生在内部 List.apply内,也许不是您所期望的那样,因为它位于List.apply之外,如下所示:< / p>

    e.map(i => List(i2s(i))
    
  • .map返回的类型在某种程度上不是Either[String, List[Int => String]],而是Either[String, Int => String]。这是由于以下事实:List[A]扩展了PartialFunction[Int, A],从而允许其将结果转换为函数类型。

这不适用于Option[A],因为它不会在类型层次结构中的任何地方扩展PartialFunction

这里的关键要点是,lambda表达式的扩展无法按您的预期工作,因为List(i2s(_))扩展为List(i2s(x => i2s(x))而不是List(i => i2s(i))。有关下划线扩展的更多信息,请参见What are all the uses of an underscore in Scala?