DAY 13
0
Software Development

# Algebraic Data Type 的 0 與 1

``````0             Nothing
1             Unit
2 = 1 + 1     Boolean = True | False
a + b         Either<a, b>
a * b         Pair<a, b>
a + 1         Maybe<a>
``````

# Hybrid Algebraic Data Type

``````// Product Type: r * g * b
data class Color(val r: Channel, val g: Channel, val b: Channel)
// Sum Type: 0, 1, 2, ...255
typealias Channel = Byte
``````

``````a * (b + c) = (a * b) + (a * c)
``````

``````a * (b + c)       => Pair<A, Either<B, C>>
(a * b) + (a * c) => Either<Pair<A, B>, Pair<A, C>>
``````

``````a * (b + 1) = a * b + a

a * (b + 1) => Pair<A, Maybe<B>>
a * b + a   => Either<Pair<A, B>, A>
``````

Maybe 消失了！在左邊的式子 `a * (b + 1)` 中，將 1 的加法轉換成 Maybe ，但是右邊的式子卻看不到它的存在， 這是怎麼回事呢？來分析看看吧！左邊式子的型別為 `Pair<A, Maybe<B>>` ，也就是會先處理完 `Maybe<B>` 的 case ，再回來跟 `A` 來做 Pair。現在假設 Maybe 經過 `fold` 的操作過後，回傳的型別是 C（fold 在前幾篇中有介紹過）。那我們就得到了 `Pair<A, C>` 。再來看第二個式子，其實 Either 也可以使用 `fold` 的，那如果 `fold` 之後回傳的型別是 `Pair<A, C>` 的話，不就是得到同一個資料 `Pair<A, C>` 了嗎？

``````val f: (B) -> C = {...}
val g: () -> C =  {...}

val result1: Pair<A, C> = maybeB.fold(some = f, none = { g() })
val result2: Pair<A, C> = eitherPair.fold(
left: (Pair<A, B> -> Pair<A, C>) = { it.first to f(it.second) },
right: (A -> Pair<A, C>) = { it to g()})

// 兩個 result 都同時使用了 f 與 g ，除此之外沒有執行任何其他函式。
``````

# 我們可以從 Algebraic Data Type 上學到甚麼？

``````data class ResultA(val data: String?, val fail: Throwable?) {
fun isSuccess(): Boolean {
return fail == null && data != null
}
}

sealed class ResultB() {
class Success(val data: String): Result()
class Fail(val error: Throwable): Result()
}
``````

``````typealias Result<T> = Either<Throwable, T>

// 不需要 cast 就能直接對值做操作
val result: Result<String> = getResult()
val mapResult: Result<Int> = result.mapRight { s: String -> s.length }
mapResult.fold(right = { ...}, left = { ... })
``````

# 挑戰時間

``````sealed class Either<A, B>() {

class Left<A, B>(val value: A): Either<A, B>()
class Right<A, B>(val value: B): Either<A, B>()

//TODO implement mapLeft, mapRight, fold
}

val a = Either.Left<Int, String>(1)
val b = Either.Right<Int, String>("1")

// [left = "1"]
val a2: Either<String, String> = a.mapLeft { it.toString() }
// [left = 1]
val a3: Either<Int, String> = a.mapRight { it + " this should not be executed"}
// [right = 1]
val b2: Either<Int, Int> = b.mapRight { it.length }

// c = "1 fold right"
val c: String = b.fold(left = {
it.toString() + " fold left"
},right = {
it + " fold right"
})

// d = "1 fold left"
val d: String = a2.fold(left = {
it.toString() + " fold left"
},right = {
it + " fold right"
})
``````