为什么强调在monad的某些场景中不起作用?

时间:2018-02-10 10:19:19

标签: scala

下划线_不适用于以下代码中的某些情况:

case class Item
(
  para: Int
)

val a = List(1, 2)
val b = List(Item(1), Item(2))

a foreach {
  perA => println(perA * 2) // Line A: ok
}

a foreach {
  println(_) // Line B: ok
}

a foreach {
  println(_ * 2) // Line C: not ok
}

b foreach {
  perB => println(perB.para) // line D: ok
}

b foreach {
  println(_.para) // line E: not ok
}

有人可以向我解释关于C&线的问题吗? E行,谢谢。

1 个答案:

答案 0 :(得分:3)

TL; DR: lambda的参数可以插入到意外的位置。而不是采取

a foreach { println(_.blah) }

并建立

a foreach { x => println(x.blah) }

,编译器改为构建

a foreach { println( x => x.blah ) }

然后无法派生参数x的类型。

A)这是写下lambda的最明确的方法,唯一能让它更清晰的方法就是添加一个类型:

a foreach { perA => println(perA * 2) }

相同
a foreach { (x: Int) => println(x * 2) }

这显然有效。

B)这是有效的,因为它是记录从println自动生成的函数的一种方法。它相当于以下六种变体中的任何一种:

a foreach { (x: Int) => println(x) }
a foreach { x => println(x) }
a foreach { println(_) }
a foreach { println _ }
a foreach { println }
a foreach println

C)由于* 2,此处为

a foreach { println(_ * 2) }

不再仅仅被视为println(_)。相反,它被解释为

a foreach { println( { (x: ??!) => x * 2 } ) }

并且由于println不接受函数值参数,因此它无法确定x的类型应该是什么,并以错误退出。

D)基本上与 A 相同,它有效,我希望它清楚。

E) C 的变体,但这一次,类型检查器不是在寻找方法*(i: Int)的东西,但是相反,它正在寻找成员para

这里:

b foreach { println(_.para) }

再次被解释为foreach,其函数忽略b的元素并返回表达式println(_.para)的常量值, 那就是:

b foreach { println( { (x: ??!) => x.para } ) }

同样,内部表达式println( { (x: ??!) => x.para } )没有任何意义,因为println不期望函数值的参数(它可以处理Any,但它还不够派生x)的类型。