Currently one can compose 2 functions together by
val func3 = func1 compose func2
I am suppose to be able to compose list of function by
val func4 = listOf(func1, func2, func3).reduce {a, b -> a compose b}
but currently this is impossible due to various type error.
Am I missing something? Is there a way to somehow achieve this. Or is there a way for me to write my own compose that accept list?
It sounds like something that should be possible. Can you please re-do the example, this time with type annotations for each function and the whole list?
val add5 = { i: Int -> Option(i + 5) }
val multiplyBy2 = { i: Int -> i * 2 }
fun isOdd(x: Option<Int>) = x.map { y -> y % 2 != 0 }
val composed = listOf(::isOdd, add5, multiplyBy2).reduce { a, b -> a compose b }
Error:
Type inference failed: Cannot infer type parameter IP in inline infix fun
((IP) -> R).compose(crossinline f: (P1) -> IP): (P1) -> R
None of the following substitutions
receiver: (Any) -> Any arguments: ((Nothing) -> Any)
receiver: (Nothing) -> Any arguments: ((Nothing) -> Nothing)
can be applied to
receiver: Function1<, Any> arguments: (Function1<, Any>)
So I try:
val composed = listOf<(Any) -> Any>(::isOdd, add5, multiplyBy2).reduce { x, y -> x compose y }
Error:
Type mismatch: inferred type is KFunction1<@ParameterName Option
, Option > but (Any) -> Any was expected
Type mismatch: inferred type is (Int) -> Optionbut (Any) -> Any was expected
Type mismatch: inferred type is (Int) -> Int but (Any) -> Any was expected
This is hard to explain but it's just not possible to do an upcast to List<(Any) -> Any> and compose any two functions together, at least not without (unsafe?) casting. I'm just glad it's a compile time error rather than a runtime ClassCastException.
Try using TupleN or HList instead, although I'm not sure those have a reduce function at the moment :/
To reduce a List we need that list be a list of Monoids. You can read about them in the awesome documentation here https://arrow-kt.io/docs/typeclasses/monoid/#monoid, but the explanation of your issue is hidden behind one properties of monoid: the closure. That means that monoid operation should return the same type it was applied to. functions (T) -> R and (R) -> S have different type as their composition (T) -> S, so, generally, composition of functions is not monoid. But the composition of fuctions can be monoid if we restrict that functions return the same type they applied to. So if we have plus2 : (Int) -> Int and powerOfTen : (Int) -> Int, then they are a monoid with the id as the zero value can compose a list of these functions correctly.
@pakoito Should this be closed? It seems like their feature request can't be fulfilled.
Agreed!
Most helpful comment
To reduce a List we need that list be a list of Monoids. You can read about them in the awesome documentation here https://arrow-kt.io/docs/typeclasses/monoid/#monoid, but the explanation of your issue is hidden behind one properties of monoid: the closure. That means that monoid operation should return the same type it was applied to. functions (T) -> R and (R) -> S have different type as their composition (T) -> S, so, generally, composition of functions is not monoid. But the composition of fuctions can be monoid if we restrict that functions return the same type they applied to. So if we have plus2 : (Int) -> Int and powerOfTen : (Int) -> Int, then they are a monoid with the id as the zero value can compose a list of these functions correctly.