编写函数文字时通过下划线进行 Scala 部分应用
Scala partial application via underscore when composing function literals
我正在编写函数文字,尽管与我见过的大多数示例不同,我从一个多参数函数开始,然后进行柯里化。
我有:
//types
case class Thing1(v: Double)
case class Thing2(v: Double)
case class Thing3(v: Double)
type Multiplier = Double
//functions
val f1 = (t: Thing1, m: Multiplier) => Thing2(m * t.v)
val f2 = (t: Thing2) => Thing3(t.v)
我想组合 f1 和 f2 以获得组合函数
Thing1 => (Multiplier => Thing3)
不出所料,以下代码无法编译:
val fcomposed1 = f1.curried.andThen(f2) // does not compile
通过实验,我能够确定以下内容确实可以编译并且具有正确的 fcomposed 签名:
val fcomposed2 = f1.curried(_:Thing1).andThen(f2)
我已经阅读了 What are all the uses of an underscore in Scala? and possibly relevant Why does Scala apply thunks automatically, sometimes? 等各种来源,但不幸的是,我仍然无法一步一步地弄清楚这里发生了什么以及它为什么有效。
此外,我希望上面分成两个表达式的工作方式与 fcomposed2 相同,但是第二个不编译:
val f1partial = f1.curried(_:Thing1)
val fcomposed3 = f1partial.andThen(f2) // does not compile - same error as fcomposed1
看起来 f1partial returns 与 f1.curried 具有相同的签名,这让我进一步想知道早期的 fcomposed2 是如何工作的。
有人可以逐步解释这两种行为吗?
在这里,_
充当 lambda 表达式的语法糖,但其级别可能出乎您的意料。
f1.curried
具有类型 Thing1 => Multiplier => Thing2
f1.curried(_:Thing1)
等同于 { x: Thing1 => f1.curried(x) }
。由于 f1.curried(x)
的结果类型为 Multiplier => Thing2
,因此整个表达式的最终类型仍然是 Thing1 => Multiplier => Thing2
。所以在结果(f1partial
)上调用andThen(f2)
是无效的,因为函数f2(Thing2
)的输入类型与前一个函数(Multiplier => Thing2
).
相比之下,f1.curried(_:Thing1).andThen(f2)
扩展为 { x: Thing1 => f1.curried(x).andThen(f2) }
。如前所述,f1.curried(x)
的计算结果为类型 Multiplier => Thing2
,因此您 可以 对其调用 andThen(f2)
,从而得到 Multiplier => Thing3
。那么整个表达式的计算结果为 Thing1 => Multiplier => Thing3
如果您考虑一下这两个表达式之间的区别,也许会更清楚:
val fcomposed1 = { x: Thing1 => f1.curried(x).andThen(f2) } // valid
val fcomposed2 = { x: Thing1 => f1.curried(x) }.andThen(f2) // error
我正在编写函数文字,尽管与我见过的大多数示例不同,我从一个多参数函数开始,然后进行柯里化。
我有:
//types
case class Thing1(v: Double)
case class Thing2(v: Double)
case class Thing3(v: Double)
type Multiplier = Double
//functions
val f1 = (t: Thing1, m: Multiplier) => Thing2(m * t.v)
val f2 = (t: Thing2) => Thing3(t.v)
我想组合 f1 和 f2 以获得组合函数
Thing1 => (Multiplier => Thing3)
不出所料,以下代码无法编译:
val fcomposed1 = f1.curried.andThen(f2) // does not compile
通过实验,我能够确定以下内容确实可以编译并且具有正确的 fcomposed 签名:
val fcomposed2 = f1.curried(_:Thing1).andThen(f2)
我已经阅读了 What are all the uses of an underscore in Scala? and possibly relevant Why does Scala apply thunks automatically, sometimes? 等各种来源,但不幸的是,我仍然无法一步一步地弄清楚这里发生了什么以及它为什么有效。
此外,我希望上面分成两个表达式的工作方式与 fcomposed2 相同,但是第二个不编译:
val f1partial = f1.curried(_:Thing1)
val fcomposed3 = f1partial.andThen(f2) // does not compile - same error as fcomposed1
看起来 f1partial returns 与 f1.curried 具有相同的签名,这让我进一步想知道早期的 fcomposed2 是如何工作的。
有人可以逐步解释这两种行为吗?
在这里,_
充当 lambda 表达式的语法糖,但其级别可能出乎您的意料。
f1.curried
具有类型 Thing1 => Multiplier => Thing2
f1.curried(_:Thing1)
等同于 { x: Thing1 => f1.curried(x) }
。由于 f1.curried(x)
的结果类型为 Multiplier => Thing2
,因此整个表达式的最终类型仍然是 Thing1 => Multiplier => Thing2
。所以在结果(f1partial
)上调用andThen(f2)
是无效的,因为函数f2(Thing2
)的输入类型与前一个函数(Multiplier => Thing2
).
相比之下,f1.curried(_:Thing1).andThen(f2)
扩展为 { x: Thing1 => f1.curried(x).andThen(f2) }
。如前所述,f1.curried(x)
的计算结果为类型 Multiplier => Thing2
,因此您 可以 对其调用 andThen(f2)
,从而得到 Multiplier => Thing3
。那么整个表达式的计算结果为 Thing1 => Multiplier => Thing3
如果您考虑一下这两个表达式之间的区别,也许会更清楚:
val fcomposed1 = { x: Thing1 => f1.curried(x).andThen(f2) } // valid
val fcomposed2 = { x: Thing1 => f1.curried(x) }.andThen(f2) // error