我正在查看找到here的箭头库。为什么要使用Option
类型而不是Kotlin内置的nullables?
答案 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似乎有所不同,并希望始终使用它们的逻辑。
答案 1 :(得分:1)
一年多来,我一直在使用Arrow提供的Option
数据类型,从一开始,我们就对自己做了完全相同的问题。答案如下。
如果仅将option
数据类型与nullables
进行比较,它们几乎是偶数。相同的语义(是否有值),几乎相同的语法(对于Option,您使用map
,对于nullables您使用safe call operator)。
但是使用Options
,您可以从箭头生态系统中受益!
使用Options
时,您正在使用Monad Pattern
。当将monad模式与arrow,scala cats,scalaz之类的库一起使用时,您可以从几个功能概念中受益。仅有3个好处示例(还有很多其他好处):
(尝试用于表达和避免引发异常),尝试,验证,IO等
您可以轻松地将一个单子转换为另一个。您有一个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) }
}
}
有点复杂,但非常有用且易于使用。
这不仅仅是将安全呼叫运算符(可为空)更改为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
我强烈建议您开始使用Option
,Either
等单子词,即使您不确定该概念是否适合您的语义学,也要尽快使用它。可以从功能性生态系统中受益,或者如果您还不了解的话。很快,您将无需注意学习曲线就可以使用它。在我的公司中,我们几乎在所有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>
,就不会有问题。