为什么使用Arrow的选项而不是Kotlin可以为空

时间:2018-02-20 22:12:45

标签: kotlin functional-programming arrow-kt

我正在查看找到here的箭头库。为什么要使用Option类型而不是Kotlin内置的nullables?

3 个答案:

答案 0 :(得分:10)

免责声明:如果您真的希望详细讨论为什么箭头有用,请转到https://soundcloud.com/user-38099918/arrow-functional-library并听取其中一个从事这项工作的人。 (5:35分钟)

创建和使用该库的人们想要使用Kotlin的方式与创建它的人不同,并使用"the Option datatype similar to how Scala, Haskell and other FP languages handle optional values"

这只是定义您不知道输出的值的返回类型的另一种方法。

让我向您展示三个版本:

Kotlin的可空性

val someString: String? = if (condition) "String" else null

具有其他值的对象

val someString: String = if (condition) "String" else ""

箭头版

val someString: Option<String> = if (condition) Some("String") else None

Kotlin逻辑的一个主要部分可能是永远不要像String?那样使用nullable types,但是在与Java交互时你需要使用它。这样做时,您需要使用string?.split("a")not-null assertion string!!.split("a")之类的安全通话。

我认为在使用Java库时使用安全调用是完全有效的,但Arrow guys似乎有所不同,并希望始终使用它们的逻辑。

使用箭头逻辑的好处是"empowering users to define pure FP apps and libraries built atop higher order abstractions. Use the below list to learn more about Λrrow's main features"

答案 1 :(得分:1)

一年多来,我一直在使用Arrow提供的Option数据类型,从一开始,我们就对自己做了完全相同的问题。答案如下。

选项与可为空

如果仅将option数据类型与nullables进行比较,它们几乎是偶数。相同的语义(是否有值),几乎相同的语法(对于Option,您使用map,对于nullables您使用safe call operator)。

但是使用Options,您可以从箭头生态系统中受益!

箭头生态系统(功能生态系统)

使用Options时,您正在使用Monad Pattern。当将monad模式与arrowscala catsscalaz之类的库一起使用时,您可以从几个功能概念中受益。仅有3个好处示例(还有很多其他好处):

1。访问其他Monad(不是唯一的选项)

(尝试用于表达避免引发异常),尝试,验证,IO等

2。单子+抽象之间的转换

您可以轻松地将一个单子转换为另一个。您有一个Try,但想返回(并表达)一个Either吗?只是转换为它。您有Either,但不关心该错误吗?只需转换为Option

val foo = Try { 2 / 0 }
val bar = foo.toEither()
val baz = bar.toOption()

此抽象还可以帮助您创建不关心容器(monad)本身而仅关心内容的函数。例如,您可以通过以下方式创建一种扩展方法Sum(bigDecimal,anotherBigDecimal),该扩展方法适用于 ANY MONAD (更精确地说:“到任何应用实例” ):

fun <F> Applicative<F>.sum(vararg kinds: Kind<F, BigDecimal>): Kind<F, BigDecimal> {
    return kinds.reduce { kindA, kindB ->
        map(kindA, kindB) { (a, b) -> a.add(b) }
    }
}

有点复杂,但非常有用且易于使用。

3。 Monad的理解力

这不仅仅是将安全呼叫运算符(可为空)更改为map个呼叫(在Option中)。看一看箭头作为模式“ Monad Comprehensions”的实现提供的“绑定”功能:

fun calculateRocketBoostComprehensive(rocketStatus: RocketStatus): Option<Double> {
    return binding {
        val (gravity) = rocketStatus.gravity
        val (currentSpeed) = rocketStatus.currentSpeed
        val (fuel) = rocketStatus.fuel
        val (science) = calculateRocketScienceStuff(rocketStatus)
        val fuelConsumptionRate = Math.pow(gravity, fuel)
        val universeStuff = Math.log(fuelConsumptionRate * science)

        universeStuff * currentSpeed
    }
}

以上示例中的所有属性/函数返回均为Options。在binding键控的内部,map调用对我们来说是抽象的。该代码更易于阅读(和编写),并且您无需检查值是否存在,如果其中一些值不存在,则计算将停止并且结果将为带有None的Option。

请以空验证代替此代码。不仅safe call operators,而且可能还有if null then return代码路径。不是很多吗?

此外,上面的示例使用Option,但是将monad理解作为抽象的真正力量在于将其与IO之类的monad一起使用时,您可以在其中完全相同地抽象异步代码执行“干净,按顺序和命令式”的方式:O

结论

我强烈建议您开始使用OptionEither等单子词,即使您不确定该概念是否适合您的语义学,也要尽快使用它。可以从功能性生态系统中受益,或者如果您还不了解的话。很快,您将无需注意学习曲线就可以使用它。在我的公司中,我们几乎在所有Kotlin项目中都使用了它,即使是在面向对象的项目中(大多数)。

答案 2 :(得分:0)

没有提到其他答案的一件事:您可以拥有Option<Option<SomeType>>,而不能拥有SomeType??。或者Option<SomeType?>。这对于组合性非常有用。例如。考虑Kotlin's Map.get

abstract operator fun get(key: K): V?
     

返回与给定键对应的值,如果地图中不存在此键,则返回null

但是如果V是可为空的类型怎么办?然后,当get返回null时,可能是因为映射为给定键存储了空值,或者是因为没有值;你不能说!如果它返回Option<V>,就不会有问题。