fun mean(xs: List<Double>): Either<String, Double> = if (xs.isEmpty()) Either.Left("mean of empty list!") else Either.Right(xs.sum() / xs.size) either { val x = mean(listOf(1.0, 2.0, 3.0)).bind() // Right(2.0) val y = mean(listOf(4.0, 5.0)).bind() // Right(4.5) x + y } // => Right(6.5) either { val x = mean(listOf(1.0, 2.0, 3.0)).bind() // Right(2.0) val y = mean(emptyList()).bind() // Left(...) x + y } // => Left("mean of empty list!") either メソッド 13
fun mean(xs: List<Double>): Result<Double, String> = if (xs.isEmpty()) Err("mean of empty list!") else Ok(xs.sum() / xs.size) binding { val x = mean(listOf(1.0, 2.0, 3.0)).bind() // Ok(2.0) val y = mean(listOf(4.0, 5.0)).bind() // Ok(4.5) x + y } // => Ok(6.5) binding { val x = mean(listOf(1.0, 2.0, 3.0)).bind() // Ok(2.0) val y = mean(emptyList()).bind() // Err(...) x + y } // => Err("mean of empty list!") binding メソッド 14
汎用的なシンタックスシュガー) が便利 Scala 標準ライブラリの Either def mean(xs: Seq[Double]): Either[String, Double] = if xs.isEmpty then Left("mean of empty list!") else Right(xs.sum / xs.length) for x <- mean(Seq(1, 2, 3)) // Right(2) y <- mean(Seq(4, 5)) // Right(4.5) yield x + y // => Right(6.5) for x <- mean(Seq(1, 2, 3)) // Right(2) y <- mean(Seq(4, 5)) // Left("mean of empty list!") yield x + y // => Left("mean of empty list!") for 式 15
interface ImportResult<out T> { data class Success<out T>(val value: T) : ImportResult<T> data class Failure(val errors: List<ImportError>) : ImportResult<Nothing> { init { // 代わりにnon-empty list を用意してもよい require(errors.isNotEmpty()) } } val isSuccess: Boolean get() = this is Success val isFailure: Boolean get() = this is Failure // 以下、便利な関数を定義( 後述) } 17
概念的には、空(nil) もしくは要素を持つ(cons) 再帰的な代数的データ型 典型的な操作でwhen による場合分けが不要に fun <U> fold(valueFn: (T) -> U, errorsFn: (List<ImportError>) -> U): U = when (this) { is Success -> valueFn(value) is Failure -> errorsFn(errors) } 19