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]
请说明区别。
答案 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?