减少与然后跨越不同类型的功能

eva*_*rks 9 scala

我想以编程方式组成几个函数.如果这些函数都是相同的类型,我可以执行以下操作:

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 _)
Run Code Online (Sandbox Code Playgroud)

在这一点f1并且f2是相同的东西,这编译因为List定义f2List[Function1[Int,Int]]

但是,如果我想使用相同的基本reduce技术将不同类型的多个兼容函数链接在一起,我会收到错误.

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 _)
Run Code Online (Sandbox Code Playgroud)

第二个选项不能编译,因为定义的列表f4被推断为a List[Function1[Any,Any]],但我无法弄清楚是否有一个干净的类型安全的方式来获取表单函数的有序集合Function1[A,B],Function1[B,C],Function1[C,D],...,Function1[X,Y]并将它们粘合在一起Function1[A,Y]就像这样.

有任何想法吗?

Tra*_*own 13

这里有两个问题.第一个(正如你所注意到的)是列表有一个单独的元素类型,它将被推断为它所包含的元素类型的最小上限,在这种情况下,它是非常无聊和无用的String with Int with Double => Any.异构列表提供了解决这部分问题的一种方法,我将在一秒钟内展示.

第二个问题是,_ andThen _多态性不足(正如Bob Dalgleish在上面的评论中指出的那样).参数reduce将是一个具有具体输入类型和具体输出类型的函数,所以即使我们有一个异构列表,我们也无法用FunctionScala标准库来减少它- 我们需要一个多态函数值代替.

幸运的是(如果你真的想在Scala中做这种事情),有一个很棒的库叫做Shapeless,可以很好地实现异构列表和多态函数.例如,您可以编写以下内容:

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 _)
}
Run Code Online (Sandbox Code Playgroud)

然后:

scala> val andThenned = HList((d _), (e _), (f _)).reduceLeft(andThen)
andThenned: Double => Double = <function1>

scala> andThenned(13.0)
res0: Double = 52.0
Run Code Online (Sandbox Code Playgroud)

我觉得这很漂亮.