0
votes

Currently I am using compose from a library called arrow which has it defined this way.

inline infix fun <IP, R, P1> ((IP) -> R).compose(crossinline f: (P1) -> IP): (P1) -> R = { p1: P1 -> this(f(p1)) }

What I am trying to do is compose functions from a list so I assumed something as simple as this would work.

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  }

but I get type 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 }

and I get this:

Type mismatch: inferred type is KFunction1<@ParameterName Option, Option> but (Any) -> Any was expected

Type mismatch: inferred type is (Int) -> Option but (Any) -> Any was expected

Type mismatch: inferred type is (Int) -> Int but (Any) -> Any was expected

Any help appreciated. I don't mind if I end up having to write my own version of compose. I just need to be able to compose a list of functions.

edit:

This works no problems:

val composed = ::isOdd compose add5 compose multiplyBy2

I am just trying to achieve the same result should I have a list of functions instead of writing this way.

1

1 Answers

3
votes

I find it hard to imagine how a simple compose should work with methods having so different signatures. So first we would have to align the types of the functions. Arrow let's you compose functions if the return type of the first matches the parameter of the second...

Another problem is that isOdd is a Predicate. It is not transforming a value.

If the transformers have a compatible signature you can compose them with e.g. andThen

Here is a version that aligns the types to compose the functions. Note that filter and map are special functions in arrow's Option that allow you to pass transformer functions/predicates

import arrow.core.Option
import arrow.core.andThen
import org.hamcrest.Matchers.`is`
import org.junit.Assert.assertThat
import org.junit.Test

class ComposeTest {

    @Test
    fun shouldCompose() {
        val add5 = { i: Int -> i + 5 }
        val multiplyBy2 = { i: Int -> i * 2 }
        val isOdd = { x: Int -> x % 2 != 0 }

        val composed: (Int) -> Option<Int> = { i: Int -> Option.just(i)
          .filter(isOdd)
          .map(add5.andThen(multiplyBy2))
        }

        assertThat(composed(3), `is`(Option.just(16)))
        assertThat(composed(4), `is`(Option.empty()))
    }
}