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

pav*_*163 9 functional-programming kotlin arrow-kt

我正在看看这里找到的Arrow库.为什么要使用一种Option类型而不是Kotlin的内置nullables?

cre*_*not 11

免责声明:如果您真的想详细讨论为什么Arrow有用,请转到https://soundcloud.com/user-38099918/arrow-functional-library并听取其中一位工作人员的意见.(5:35分钟)

创建和使用该库的人们想要使用Kotlin的方式与创建它的人不同,并使用"类似于Scala,Haskell和其他FP语言处理可选值的选项数据类型".

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

让我告诉你三个版本:

Kotlin的可空性

val someString: String? = if (condition) "String" else null
Run Code Online (Sandbox Code Playgroud)

具有另一个值的对象

val someString: String = if (condition) "String" else ""
Run Code Online (Sandbox Code Playgroud)

箭头版

val someString: Option<String> = if (condition) Some("String") else None
Run Code Online (Sandbox Code Playgroud)

科特林逻辑的主要部分可从不使用可空类型一样String?,但与Java interopting当你需要使用它.这样做时,您需要使用安全调用string?.split("a")或非空语句 string!!.split("a").

我认为在使用Java库时使用安全调用是完全有效的,但是Arrow人似乎认为不同并且想要一直使用他们的逻辑.

使用Arrow逻辑的好处是"使用户能够定义在更高阶抽象上构建的纯FP应用程序和库.使用下面的列表来了解有关Λrrow主要功能的更多信息".

  • 对于“为什么我应该使用`Option`而不是可空值”的问题,我希望有具体的原因/优势,特别是考虑到额外的语法开销和不符合标准类型。然而,这个答案只是说明“所以你可以以不同的方式使用 Kotlin”。 (7认同)

fab*_*tto 8

我已经使用Option了Arrow所提供的数据类型已有一年多了,一开始我们对自己也提出了完全相同的问题。答案如下。

期权vs可空

如果仅在Kotlin 中将option数据类型与进行比较nullables,它们几乎是偶数。相同的语义(是否有值),几乎相同的语法(使用Option时使用map,使用null时使用安全调用运算符)。

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

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

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

1.访问其他单子(不是唯一的选项)

(对表达避免抛出异常很有用),尝试,验证,IO等。非常常见的monad,它们可以帮助我们(以更好的方式)完成典型项目中的工作。

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

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

val foo = Try { 2 / 0 }
val bar = foo.toEither()
val baz = bar.toOption()
Run Code Online (Sandbox Code Playgroud)

这种抽象还可以帮助您创建不关心容器(monad)本身,仅关心内容的函数。例如,您可以通过以下方式创建Sum(bigDecimal,anotherBigDecimal)可与ANY MONAD配合使用的扩展方法(更确切地说:“至任何applicative实例”):

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) }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

3. Monad的理解

这不仅仅是将安全呼叫操作员(使用nullable时)更改为map呼叫(使用Options时)。看一看箭头作为模式“ 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
    }
}
Run Code Online (Sandbox Code Playgroud)

上例中使用的所有功能以及rocketStatus参数的属性均为Options。在binding块内,flatMap为我们抽象了呼叫。该代码更易于阅读(和编写),并且您无需检查值是否存在,如果其中某些值不存在,则计算将停止并且结果将为Option None

现在尝试想象一下此代码具有空验证。不仅safe call operatorsif null then return代码路径,而且可能还有代码路径。是不是更难了?

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

结论

我强烈建议您在看到概念适合您想要的语义学后立即开始使用诸如OptionEither等之类的monad,即使您不确定是否会从功能生态系统中获得其他重大好处或是否不了解它们很好 很快,您将无需注意学习曲线就可以使用它。在我的公司中,我们几乎在所有Kotlin项目中都使用了它,即使是在面向对象的项目中(大多数)。

  • @Fleshgrinder,这可能是个人喜好,但对我来说,这段代码比我的示例中显示的命令式版本更难理解和调试。此外,如果您需要编写扩展函数(显然是为了维护它),那么它也是有成本的。 (2认同)

Ale*_*nov 5

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

abstract operator fun get(key: K): V?
Run Code Online (Sandbox Code Playgroud)

返回与给定键对应的值,或者null如果映射中不存在这样的键。

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