iT邦幫忙

2023 iThome 鐵人賽

DAY 29
0

Monad 和 Applicative Functors 的差異

假設我們用 Option 來從 Map 資料中找東西,2 個查詢彼此獨立,通常可以輕鬆的用 map2 把結果合併起來,

  val F: Applicative[Option] = ...
  val depts: Map[String, String] = ...
  val salaries: Map[String, Double] = ...
  val o: Option[String] =
    F.map2(depts.get("Steven"), salaries.get("Steven"))(
      (dept, salary) => s"Steven in $dept make $salriy per year"
    )

但如果我們的查詢彼此有依賴關係呢?像下面的例子,我們需要先用名字找到員工 ID,然後在用員工 ID 找到部門和薪水,此時就會需要 flatMap 或 join 來達成此需求了,

  val idsByName: Map[String, Int] = ...
  val departments: Map[Int, String] = ...
  val salaries: Map[Int, Double] = ...
  val o: Option[String] =
    idsByName.get("Steven").flatMap(id =>
      F.map2(departments.get(id), salaries.get(id))(
        (dept, salary) => s"Steven in $dept make $salriy per year"
      )
    )

這裡就可以看出主要差別了,Applicative 的計算具有固定結構和單純的順序作用,而 Monad 計算可以動態的選擇先前資料,也就是說支持上下文有關係的計算。

不是所有的 Applicative Functors 都是 Monad

讓我們用 Day 7 的 Either 來舉例它做為 Applicative 比 Monad 還來得好,

假設我們使用 validName、validBirthdate 和 validPhone 來檢查表格資料,每個方法回傳的型態是 Either[String, T],若我們想一次性的檢查所有欄位,用 map3 合併是個相當合情合理的操作,

map3(validName(field1),
  validBirthdate(field2),
  validPhone(field3))(WebForm(_,_,_))

但 Monad 的 map3 跟 map2 一樣,都是用 flatMap 實現,

  extension[A] (fa: F[A])
    def map2[B, C](fb: F[B])(f: (A, B) => C): F[C] =
      fa.flatMap(a => fb.map(b => f(a, b)))

		def map3[B, C, D](fb: F[B], fc: F[C])(f: (A, B, C) => D): F[D] =
      fa.flatMap(a => fb.flatMap(b => fc.map(c => f(a, b, c))))

這時候就糗了,細部拆解的話 map3 會以下面這個順序調用,

validName(field1).flatMap (f1 =>
validBirthdate(field2).flatMap (f2 =>
validPhone(field3).map(f3 => 
              WebForm(f1, f2, f3))

這使得只要前面有任一是 Left,後續的欄位檢查就不會執行,這樣會導致我們的欄位檢查一次只能回傳一個錯誤;

那如果是用 Applicative Functor 的 map3 呢,昨天 Exercise D28-2 的 map3 我們用 apply 和 unit 來實現,

  extension[A] (fa: F[A])
    def map3[B, C, D](fb: F[B], fc: F[C])(f: (A, B, C) => D): F[D] =
      apply(apply(apply(unit(f.curried))(fa))(fb))(fc)

這裡沒有依賴關係,所以 3 個欄位檢查都會執行到,並且做為參數傳給 map3 合併,這也是 Either 不是 Monad 的主要原因。


最後一天聊聊 Applicative Functor 的定律吧!


上一篇
Applicative Functors (1)
下一篇
Applicative Functors (3)
系列文
用 Scala 3 寫的 Functional Programming 會長什麼樣子?30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言