我想以编程方式编写几个函数。如果这些函数都属于同一类型,我可以执行以下操作:
def a(x: Int): Int = x+1
def b(y: Int): Int = y/2
def c(z: Int): Int = z*4
val f1 = (a _) andThen (b _) andThen (c _)
val f2 = List((a _), (b _), (c _)).reduce(_ andThen _)
此时f1
和f2
是相同的,并且这会编译,因为定义List
的{{1}}是f2
但是,如果我想使用相同的基本reduce技术将不同类型的多个兼容函数链接在一起,我会收到错误。
List[Function1[Int,Int]]
第二个选项无法编译,因为定义def d(x: Double): Int = x.toInt
def e(y: Int): String = y.toString
def f(z: String): Double = z.toDouble*4
//Works fine
val f3 = (d _) andThen (e _) andThen (f _)
//Doesn't compile
val f4 = List((d _), (e _), (f _)).reduce(_ andThen _)
的列表被推断为f4
,但我无法弄清楚是否采用了一种干净的类型安全方式来获取有序集合List[Function1[Any,Any]]
形式的函数,并将它们粘合在一起作为Function1[A,B],Function1[B,C],Function1[C,D],...,Function1[X,Y]
这样的。
有什么想法吗?
答案 0 :(得分:13)
这里有两个问题。第一个(正如你所注意到的)是列表有一个单独的元素类型,它将被推断为它所包含的元素类型的最小上限,在这种情况下,它是非常无聊和无用的{{ 1}}。异构列表提供了解决这部分问题的一种方法,我将在一秒钟内展示。
第二个问题是String with Int with Double => Any
的多态性不足(正如Bob Dalgleish在上面的评论中指出的那样)。 _ andThen _
的参数将是具有具体输入类型和具体输出类型的函数,因此即使我们有异构列表,我们也无法使用Scala标准中的reduce
来减少它图书馆 - 我们需要polymorphic function value代替。
幸运的是(如果你真的想在Scala中做这种事情),有一个很棒的库Shapeless,它提供了异构列表和多态函数的很好的实现。例如,您可以编写以下内容:
Function
然后:
def d(x: Double): Int = x.toInt
def e(y: Int): String = y.toString
def f(z: String): Double = z.toDouble * 4
import shapeless._
object andThen extends Poly2 {
implicit def functions[A, B, C] = at[A => B, B => C](_ andThen _)
}
我认为这非常简洁。