Kotlin で Maybe その3をベースにさらに修正した。 Maybe を monad にする覚え書き。
$ kotlin -version
Kotlin version 2.0.20-release-360 (JRE 17.0.12+7-Ubuntu-1ubuntu222.04)
// main.kts
sealed class Maybe<T>(val value: T?) {
fun <R> bind(transform: (T)->Maybe<R>): Maybe<R> {
return if( value!=null ){
transform(value)
} else {
Maybe<R>.Nothing()
}
}
override fun toString(): String {
return if( value!=null ){ "Just($value)" } else { "Nothing" }
}
companion object {
fun <T> `return`(value: T?): Maybe<T> {
return if( value!=null ){
Maybe<T>.Just(value)
} else {
Maybe<T>.Nothing()
}
}
}
class Just<T>(value: T): Maybe<T>(value)
class Nothing<T>(): Maybe<T>(null)
}
bind の実装について補足: bind はその内部の処理で value が null かどうかで分岐するよりは、 when を使って Just か Nothing かで分岐した方が(気分が)よいのだが、 そうすると value の型が T? のままなので、 value!! と記述しなければいけない。 これが気持悪い。 もそれだったら、普通に if を使えばいいかな。
bind を when で実装した場合:
fun <R> bind(transform: (T)->Maybe<R>): Maybe<R> { return when(this){ is Maybe.Just -> { transform(value!!) } is Maybe.Nothing -> { Maybe<R>.Nothing() } } }
まあ、どっちでもいい。
この Maybe<T> を使うコード例。
二つの Maybe<Int> を足し合わせる関数 add :
val add: (Maybe<Int>, Maybe<Int>)-> Maybe<Int> = { maybeA, maybeB->
maybeA.bind( {a: Int->
maybeB.bind( {b: Int->
Maybe.Just(a+b)
//Maybe.`return`(a+b)
} )
} )
}
こんな風に使う:
val one: Maybe<Int> = Maybe.Just(1)
val two: Maybe<Int> = Maybe.Just(2)
val result: Maybe<Int> = add(one, two)
println(result)
実行:
$ kotlin main.kts
Just(3)
Maybe.Nothing() と足し合わせようとする場合:
val one: Maybe<Int> = Maybe.Just(1)
val nothing: Maybe<Int> = Maybe.Nothing()
val result: Maybe<Int> = add(one, nothing)
println(result)
実行:
$ kotlin main.kts
Nothing
型の異なる2つの Maybe ( Maybe<Int> と Maybe<Float> ) を足す関数 add2 :
val add2: (Maybe<Int>, Maybe<Float>)-> Maybe<Float> = { maybeA, maybeB->
maybeA.bind( {a: Int->
maybeB.bind( {b: Float->
Maybe.Just(a.toFloat() + b)
} )
} )
}
使ってみる。
val one: Maybe<Int> = Maybe.Just(1)
val two: Maybe<Float> = Maybe.Just(2f)
val result: Maybe<Float> = add2(one, two)
println(result)
実行:
$ kotlin main.kts
3f
型が (Int)->Maybe<Int> のような inc や dec 関数を用意しておけば...このように:
val inc:(Int)->Maybe<Int> = { v->
Maybe.Just( v+1 )
}
val dec:(Int)->Maybe<Int> = { v->
Maybe.Just( v-1 )
}
次のように(失敗の可能性ある計算を)記述できる。
val result: Maybe<Int> = Maybe.Just(1).bind( inc ).bind( inc ).bind( dec )
println(result)
実行:
$ kotlin main.kts
Just(2)
必ず失敗する関数 failure をつくり、計算途中に入れる例:
val failure:(Int)->Maybe<Int> = { v->
Maybe.Nothing()
}
val result: Maybe<Int> = Maybe.Just(1).bind( inc ).bind( failure ).bind( inc ).bind( dec )
println(result)
実行:
$ kotlin main.kts
Nothing
ここで書いたコード全体を掲載します。
// main.kts
sealed class Maybe<T>(val value: T?) {
fun <R> bind(f: (T)->Maybe<R>): Maybe<R> {
return when(this){
is Maybe.Just -> {
f(value!!)
}
is Maybe.Nothing -> {
Maybe<R>.Nothing()
}
}
/*
return if( value!=null ){
f(value)
} else {
Maybe<R>.Nothing()
}
*/
}
override fun toString(): String {
return if( value!=null ){ "Just($value)" } else { "Nothing" }
}
companion object {
fun <T> `return`(value: T?): Maybe<T> {
return if( value!=null ){
Maybe<T>.Just(value)
} else {
Maybe<T>.Nothing()
}
}
}
class Just<T>(value: T): Maybe<T>(value)
class Nothing<T>(): Maybe<T>(null)
}
val add: (Maybe<Int>, Maybe<Int>)-> Maybe<Int> = { maybeA, maybeB->
maybeA.bind( {a: Int->
maybeB.bind( {b: Int->
Maybe.Just(a+b)
} )
} )
}
val add2: (Maybe<Int>, Maybe<Float>)-> Maybe<Float> = { maybeA, maybeB->
maybeA.bind( {a: Int->
maybeB.bind( {b: Float->
Maybe.Just(a.toFloat() + b)
} )
} )
}
/*
// Example-1:
val one: Maybe<Int> = Maybe.Just(1)
val two: Maybe<Int> = Maybe.Just(2)
val result: Maybe<Int> = add(one, two)
println(result)
*/
/*
// Example-2:
val one: Maybe<Int> = Maybe.Just(1)
val nothing: Maybe<Int> = Maybe.Nothing()
val result: Maybe<Int> = add(one, nothing)
println(result)
*/
/*
// Example-3:
val one: Maybe<Int> = Maybe.Just(1)
val two: Maybe<Float> = Maybe.Just(2f)
val result: Maybe<Float> = add2(one, two)
println(result)
*/
// Example-4:
val inc:(Int)->Maybe<Int> = { v->
Maybe.Just( v+1 )
}
val dec:(Int)->Maybe<Int> = { v->
Maybe.Just( v-1 )
}
val failure:(Int)->Maybe<Int> = { v->
Maybe.Nothing()
}
val result: Maybe<Int> = Maybe.Just(1).bind( inc ).bind( failure ).bind( inc ).bind( dec )
println(result)
以上です。