在不同类型的函数中使用 andThen 进行归约

2024-01-14

我想以编程方式组合多个函数。如果这些函数都是同一类型,我可以执行以下操作:

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 and f2是同一件事,并且可以编译,因为List定义了f2 is a 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]并将它们粘在一起作为Function1[A,Y]像这样。

有任何想法吗?


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

第二个问题是_ andThen _多态性不够(正如 Bob Dalgleish 在上面的评论中指出的那样)。论点为reduce将是一个具有具体输入类型和具体输出类型的函数,因此即使我们有一个异构列表,我们也无法用Function来自 Scala 标准库——我们需要一个多态函数值 http://www.chuusai.com/2012/04/27/shapeless-polymorphic-function-values-1/反而。

幸运的是(如果你真的想在 Scala 中做这种事情),有一个很棒的库叫做无形的 https://github.com/milessabin/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 _)
}

进而:

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

scala> andThenned(13.0)
res0: Double = 52.0

我认为这非常整洁。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在不同类型的函数中使用 andThen 进行归约 的相关文章

随机推荐