为什么使用 Arrow 的 Options 而不是 Kotlin 的 nullable
Why use Arrow's Options instead of Kotlin nullable
我正在查看发现的 Arrow 库 here。为什么要使用 Option
类型而不是 Kotlin 内置的可空值?
免责声明:如果你真的想详细谈谈为什么 Arrow 有用,请前往 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"。
这只是定义您不知道其输出的 return 类型值的另一种方法。
让我给你看三个版本:
Kotlin 中的可空性
val someString: String? = if (condition) "String" else null
具有另一个值的对象
val someString: String = if (condition) "String" else ""
Arrow 版本
val someString: Option<String> = if (condition) Some("String") else None
Kotlin 逻辑的主要部分可以是永远不要使用 nullable types like String?
, but you will need to use it when interopting with Java. When doing that you need to use safe calls like string?.split("a")
or the not-null assertion string!!.split("a")
.
我认为在使用 Java 库时使用安全调用是完全有效的,但是 Arrow guys 似乎有不同的想法,并且一直想使用他们的逻辑。
我使用 Arrow 提供的 Option
数据类型已经一年多了,一开始,我们对自己做了完全相同的问题。答案如下。
选项与可为空
如果仅比较 option
数据类型与 Kotlin 中的 nullables
数据类型,它们几乎是相等的。相同的语义(有或没有一些值),几乎相同的语法(使用选项 map
,使用可空值 safe call operator)。
但是当使用 Options
时,您可以从 arrow 生态系统中获益!
箭头生态系统(功能生态系统)
当使用 Options
时,您正在使用 Monad Pattern
。当将 monad 模式与 arrow, scala cats, scalaz 等库一起使用时,您可以从几个函数概念中获益。仅举 3 个好处示例(还有很多):
1。访问其他 Monad
Option
不止一个!例如,Either
对于 表达 和 避免抛出 异常非常有用。 Try
、Validated
和 IO
是其他常见单子的示例,它们帮助我们(以更好的方式)完成我们在典型项目中所做的事情。
2。 monads + 抽象之间的转换
您可以轻松地将一个 monad 转换为另一个。您有一个 Try
但想要 return(并表达)一个 Either
?只是转换成它。您有 Either
但不关心错误?只需转换为 Option
.
val foo = Try { 2 / 0 }
val bar = foo.toEither()
val baz = bar.toOption()
这种抽象还可以帮助您创建不关心容器 (monad) 本身,只关心内容的函数。例如,您可以创建一个扩展方法 Sum(anyContainerWithBigDecimalInside, anotherContainerWithBigDecimal)
与 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 理解
从 nullable 到 monad 不仅仅是将安全调用运算符更改为 map
调用。看看箭头提供的“绑定”功能作为模式“Monad Comprehensions”的实现:
fun calculateRocketBoost(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
}
}
上例中rocketStatus
参数使用的所有函数和属性都是Options
。在 binding
块内, flatMap
调用是为我们抽象的。代码更容易阅读(和编写)并且您不需要检查值是否存在,如果其中一些值不存在,计算将停止并且结果将是带有 None
的选项!
现在试着想象这段代码使用空验证。不仅 safe call operators
,而且可能 if null then return
代码路径。是不是更难了?
此外,上面的示例使用了 Option
,但是将 monad 理解作为抽象的真正力量在于当您将它与 IO 这样的 monad 一起使用时,您可以在其中准确地抽象异步代码执行与上面相同的“干净、顺序和命令式”方式:O
结论
我强烈建议您在发现概念符合您需要的语义时立即开始使用像 Option
、Either
等单子,即使您不确定是否会接受功能生态系统的其他巨大好处,或者如果您还不太了解它们。很快您就会在不注意 learning-curve 的情况下使用它。在我的公司,我们几乎在所有 Kotlin 项目中都使用它,甚至在 object-oriented 个项目(占大多数)中也是如此。
有一件事其他答案没有提到:你可以有 Option<Option<SomeType>>
而你不能有 SomeType??
。或者 Option<SomeType?>
,就此而言。这对于组合性非常有用。例如。考虑 Kotlin's Map.get
:
abstract operator fun get(key: K): V?
Returns the value corresponding to the given key, or null
if such a key is not present in the map.
但是如果 V
是一个可为 null 的类型呢?那么当 get
returns null
时,可能是因为映射为给定键存储了一个空值,或者因为没有值;你不知道!如果它返回Option<V>
,就没有问题。
我正在查看发现的 Arrow 库 here。为什么要使用 Option
类型而不是 Kotlin 内置的可空值?
免责声明:如果你真的想详细谈谈为什么 Arrow 有用,请前往 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"。
这只是定义您不知道其输出的 return 类型值的另一种方法。
让我给你看三个版本:
Kotlin 中的可空性
val someString: String? = if (condition) "String" else null
具有另一个值的对象
val someString: String = if (condition) "String" else ""
Arrow 版本
val someString: Option<String> = if (condition) Some("String") else None
Kotlin 逻辑的主要部分可以是永远不要使用 nullable types like String?
, but you will need to use it when interopting with Java. When doing that you need to use safe calls like string?.split("a")
or the not-null assertion string!!.split("a")
.
我认为在使用 Java 库时使用安全调用是完全有效的,但是 Arrow guys 似乎有不同的想法,并且一直想使用他们的逻辑。
我使用 Arrow 提供的 Option
数据类型已经一年多了,一开始,我们对自己做了完全相同的问题。答案如下。
选项与可为空
如果仅比较 option
数据类型与 Kotlin 中的 nullables
数据类型,它们几乎是相等的。相同的语义(有或没有一些值),几乎相同的语法(使用选项 map
,使用可空值 safe call operator)。
但是当使用 Options
时,您可以从 arrow 生态系统中获益!
箭头生态系统(功能生态系统)
当使用 Options
时,您正在使用 Monad Pattern
。当将 monad 模式与 arrow, scala cats, scalaz 等库一起使用时,您可以从几个函数概念中获益。仅举 3 个好处示例(还有很多):
1。访问其他 Monad
Option
不止一个!例如,Either
对于 表达 和 避免抛出 异常非常有用。 Try
、Validated
和 IO
是其他常见单子的示例,它们帮助我们(以更好的方式)完成我们在典型项目中所做的事情。
2。 monads + 抽象之间的转换
您可以轻松地将一个 monad 转换为另一个。您有一个 Try
但想要 return(并表达)一个 Either
?只是转换成它。您有 Either
但不关心错误?只需转换为 Option
.
val foo = Try { 2 / 0 }
val bar = foo.toEither()
val baz = bar.toOption()
这种抽象还可以帮助您创建不关心容器 (monad) 本身,只关心内容的函数。例如,您可以创建一个扩展方法 Sum(anyContainerWithBigDecimalInside, anotherContainerWithBigDecimal)
与 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 理解
从 nullable 到 monad 不仅仅是将安全调用运算符更改为 map
调用。看看箭头提供的“绑定”功能作为模式“Monad Comprehensions”的实现:
fun calculateRocketBoost(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
}
}
上例中rocketStatus
参数使用的所有函数和属性都是Options
。在 binding
块内, flatMap
调用是为我们抽象的。代码更容易阅读(和编写)并且您不需要检查值是否存在,如果其中一些值不存在,计算将停止并且结果将是带有 None
的选项!
现在试着想象这段代码使用空验证。不仅 safe call operators
,而且可能 if null then return
代码路径。是不是更难了?
此外,上面的示例使用了 Option
,但是将 monad 理解作为抽象的真正力量在于当您将它与 IO 这样的 monad 一起使用时,您可以在其中准确地抽象异步代码执行与上面相同的“干净、顺序和命令式”方式:O
结论
我强烈建议您在发现概念符合您需要的语义时立即开始使用像 Option
、Either
等单子,即使您不确定是否会接受功能生态系统的其他巨大好处,或者如果您还不太了解它们。很快您就会在不注意 learning-curve 的情况下使用它。在我的公司,我们几乎在所有 Kotlin 项目中都使用它,甚至在 object-oriented 个项目(占大多数)中也是如此。
有一件事其他答案没有提到:你可以有 Option<Option<SomeType>>
而你不能有 SomeType??
。或者 Option<SomeType?>
,就此而言。这对于组合性非常有用。例如。考虑 Kotlin's Map.get
:
abstract operator fun get(key: K): V?
Returns the value corresponding to the given key, or
null
if such a key is not present in the map.
但是如果 V
是一个可为 null 的类型呢?那么当 get
returns null
时,可能是因为映射为给定键存储了一个空值,或者因为没有值;你不知道!如果它返回Option<V>
,就没有问题。