I want to programmatically compose several functions. If these functions are all of the same type, I can do the following:
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 _)
At which point f1
and f2
are the same thing, and this compiles because the List
that defines f2
is a List[Function1[Int,Int]]
However, if I want to chain together multiple compatible functions with different types using the same basic reduce technique, I get an error.
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 _)
The second option doesn't compile because the list that defines f4
is inferred as a List[Function1[Any,Any]]
, but I can't figure out if theres a clean type-safe way to take an ordered collection of functions of the form Function1[A,B],Function1[B,C],Function1[C,D],...,Function1[X,Y]
and glue them together as a Function1[A,Y]
like this.
Any ideas?
There are two problems here. The first (as you've noted) is that the list has a single element type, which will be inferred to be the least upper bound of the types of the elements it contains, which in this case is the extremely boring and useless String with Int with Double => Any
. Heterogeneous lists provide one way of addressing this part of the problem, as I'll show in a second.
The second problem is that the _ andThen _
is insufficiently polymorphic (as Bob Dalgleish points out in a comment above). The argument to reduce
will be a function with a concrete input type and a concrete output type, so even if we had a heterogeneous list, there's no way we could reduce it with a Function
from the Scala standard library—we'd need a polymorphic function value instead.
Fortunately (if you really want to do this kind of thing in Scala), there's a great library called Shapeless that provides nice implementations of both heterogeneous lists and polymorphic functions. For example, you could write the following:
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 _)
}
And then:
scala> val andThenned = HList((d _), (e _), (f _)).reduceLeft(andThen)
andThenned: Double => Double = <function1>
scala> andThenned(13.0)
res0: Double = 52.0
I think this is pretty neat.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With