私は見つかったArrowライブラリを見ていた here 。 Kotlinの組み込みnullableの代わりにOption
型を使用したいのはなぜですか?
免責事項:Arrowが便利な理由について詳細な説明が必要な場合は、先に進んでください https://soundcloud.com/user-38099918/arrow-functional-library にアクセスして、作業をしている人の1人に耳を傾けます。 (5:35min)
その単純なライブラリを作成して使用する人々は、ライブラリを作成して使用する人々とは異なる方法でKotlinを使用したいと考えています "Scala、Haskell、および他のFP言語の処理に類似したOptionデータ型オプションの値 " 。
これは、出力がわからない戻り値の型を定義するもう1つの方法です。
3つのバージョンを紹介します。
Kotlinのnull可能性
_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ロジックの主要な部分は、決して__ nullable types を_String?
_のように使用しないことですが、Javaとの相互運用時には使用する必要があります。その場合、string?.split("a")
または not-null assertionstring!!.split("a")
のような安全な呼び出しを使用する必要があります。
Javaライブラリを使用するときに安全な呼び出しを使用することは完全に有効であると思いますが、 Arrow guys は異なる考え方をしていて、常にロジックを使用したいと考えています。
Arrowロジックを使用する利点は "ユーザーに純粋なFPより高次の抽象化の上に構築されたアプリとライブラリを定義する権限を与えることです。rrowの主な機能の詳細については、以下のリストを使用してください" 。
Arrowが提供するOption
データ型を1年以上使用しており、最初はまったく同じ質問を自分自身で行いました。答えは次のとおりです。
Kotlinでoption
データ型だけをnullables
と比較すると、それらはほぼ偶数です。同じ意味(値があるかどうかにかかわらず)、ほぼ同じ構文(オプションではmap
を使用し、null許容値では safe call operator を使用します)。
しかし、Options
を使用すると、arrowエコシステムのメリットを活用できる可能性があります。
Options
を使用する場合、Monad Pattern
を使用します。 arrow 、 scala cats 、 scalaz のようなライブラリでモナドパターンを使用する場合、いくつかの機能的な概念から利益を得ることができます。メリットの例は3つだけです(それ以上のものがあります)。
(expressおよび例外をスローするのを避ける例外)に役立つか、試してください、検証済み、IOなど。一般的なプロジェクトで行うことを(より良い方法で)行うのに役立つ非常に一般的なモナド。
あるモナドを別のモナドに簡単に変換できます。あなたはTry
を持っていますが、Either
を返したい(そして表現したい)ですか?それに変換するだけです。あなたはEither
を持っていますが、エラーを気にしませんか? Option
に変換するだけです。
val foo = Try { 2 / 0 }
val bar = foo.toEither()
val baz = bar.toOption()
この抽象化は、コンテナー(モナド)自体ではなく、コンテンツのみに関係しない関数を作成するのにも役立ちます。たとえば、ANY MONADで機能する拡張メソッドSum(bigDecimal,anotherBigDecimal)
を作成できます(より正確には: "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) }
}
}
理解するには少し複雑ですが、非常に役立ち、使いやすいです。
Nullableからモナドへの移行は、安全な呼び出し演算子を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
!のオプションになります。
代わりに、このコードをnull検証で想像してみてください。 safe call operators
だけでなく、if null then return
コードパスもおそらく使用できます。もっと大変ではありませんか?
また、上記の例ではOption
を使用していますが、モナド内包表記を抽象化として真の力は、非同期コードを抽象化できる [〜#〜] io [〜#〜] のようなモナドで使用する場合です。上記とまったく同じ「クリーンでシーケンシャルで命令的な」方法で実行:O
機能的なエコシステムから他の大きなメリットを享受するかどうかわからない場合でも、コンセプトが必要なセマンティクスに適合したらすぐに、Option
、Either
などのモナドの使用を開始することを強くお勧めします彼らをまだよく知っている。すぐに、学習曲線に気付かずにそれを使用するようになります。私の会社では、オブジェクト指向プロジェクト(大半)であっても、ほとんどすべてのKotlinプロジェクトで使用しています。
他の答えが言及していないことの1つ:Option<Option<SomeType>>
できない場所SomeType??
。またはOption<SomeType?>
、 そのことについては。これは構成性に非常に役立ちます。例えば。検討 KotlinのMap.get
:
abstract operator fun get(key: K): V?
指定されたキーに対応する値を返します。そのようなキーがマップに存在しない場合は
null
を返します。
しかし、V
がnull許容型である場合はどうなりますか?その場合、get
がnull
を返すのは、マップが指定されたキーのnull値を格納したか、値がなかったためです。わかりません!返された場合Option<V>
、問題はありません。