0

目前我正在使用一个名为arrow的库中的compose,它以这种方式定义。

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

我想要做的是从一个列表中组合函数,所以我假设这样简单的东西会起作用。

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  }

但我收到类型错误:

类型推断失败:无法在 inline infix fun ((IP) -> R).compose(crossinline f: (P1) -> IP): (P1) -> R 中推断类型参数 IP 没有以下替换接收器:(任何) -> 任何参数:((Nothing) -> Any) 接收者:(Nothing) -> 任何参数:((Nothing) -> Nothing) 可以应用于接收者:Function1<, Any> 参数:(Function1<, Any >)

所以我尝试:

val composed = listOf<(Any) -> Any>(::isOdd, add5, multiplyBy2).reduce { x, y -> x compose y }

我明白了:

类型不匹配:推断类型为 KFunction1<@ParameterName Option, Option> 但 (Any) -> Any 是预期的

类型不匹配:推断类型是 (Int) -> Option 但 (Any) -> Any 是预期的

类型不匹配:推断类型是 (Int) -> Int 但 (Any) -> Any 是预期的

任何帮助表示赞赏。我不介意我最终是否必须编写自己的 compose 版本。我只需要能够编写函数列表。

编辑:

这没有问题:

val composed = ::isOdd compose add5 compose multiplyBy2

如果我有一个函数列表而不是这样写,我只是想达到相同的结果。

4

1 回答 1

3

我发现很难想象一个简单的组合应该如何与具有如此不同签名的方法一起使用。所以首先我们必须对齐函数的类型。如果第一个的返回类型与第二个的参数匹配,箭头让您编写函数...

另一个问题isOddPredicate. 它没有改变价值。

如果转换器具有兼容的签名,您可以使用例如andThen

这是一个对齐类型以组成函数的版本。请注意filtermap是箭头中的特殊函数Option,允许您传递转换器函数/谓词

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()))
    }
}
于 2018-08-24T06:43:29.223 回答