如果你有Option[T]
,如果有一個爲T
一個Monoid
,然後有一個Monoid[Option[T]]
:
implicit def optionTIsMonoid[T : Monoid]: Monoid[Option[T]] = new Monoid[Option[T]] {
val monoid = implicitly[Monoid[T]]
val zero = None
def append(o1: Option[T], o2: =>Option[T]) = (o1, o2) match {
case (Some(a), Some(b)) => Some(monoid.append(a, b))
case (Some(a), _) => o1
case (_, Some(b)) => o2
case _ => zero
}
}
一旦準備好了這一點,你可以使用sum
(優於foldMap(identity)
,如由@missingfaktor建議):
List(Some(1), None, Some(2), Some(3), None).asMA.sum === Some(6)
UPDATE
實際上,我們可以使用applicatives簡化上面的代碼:
implicit def optionTIsMonoid[T : Monoid]: Monoid[Option[T]] = new Monoid[Option[T]] {
val monoid = implicitly[Monoid[T]]
val zero = None
def append(o1: Option[T], o2: =>Option[T]) = (o1 |@| o2)(monoid.append(_, _))
}
這讓我想到,我們也許可以進一步推廣到:
implicit def applicativeOfMonoidIsMonoid[F[_] : Applicative, T : Monoid]: Monoid[F[T]] =
new Monoid[F[T]] {
val applic = implicitly[Applicative[F]]
val monoid = implicitly[Monoid[T]]
val zero = applic.point(monoid.zero)
def append(o1: F[T], o2: =>F[T]) = (o1 |@| o2)(monoid.append(_, _))
}
就像那個你甚至可以列出清單列表,樹木清單,...
UPDATE2
問題的澄清使我認識到,上面的UPDATE是不正確!
首先optionTIsMonoid
的,因爲重構,是不是等同於第一個定義,因爲第一個定義將跳過None
值,而第二個將盡快返回None
,因爲是在輸入列表中的None
。但在這種情況下,這不是Monoid
!事實上,Monoid[T]
必須尊重Monoid法律,而zero
必須是identity元素。
我們應該有:
zero |+| Some(a) = Some(a)
Some(a) |+| zero = Some(a)
但是,當我使用Applicative
爲Option
提出的定義爲Monoid[Option[T]]
,這種情況並非如此:
None |+| Some(a) = None
None |+| None = None
=> zero |+| a != a
Some(a) |+| None = zero
None |+| None = zero
=> a |+| zero != a
的修復並不難,我們需要更改zero
的定義:
// the definition is renamed for clarity
implicit def optionTIsFailFastMonoid[T : Monoid]: Monoid[Option[T]] =
new Monoid[Option[T]] {
monoid = implicitly[Monoid[T]]
val zero = Some(monoid.zero)
append(o1: Option[T], o2: =>Option[T]) = (o1 |@| o2)(monoid.append(_, _))
}
在這種情況下,我們將(與T
爲Int
):
Some(0) |+| Some(i) = Some(i)
Some(0) |+| None = None
=> zero |+| a = a
Some(i) |+| Some(0) = Some(i)
None |+| Some(0) = None
=> a |+| zero = zero
這證明法律身份驗證(我們也應該驗證associative法律得到尊重,...)。
現在我們有2 Monoid[Option[T]]
,我們可以隨意使用,這取決於我們在總結列表時想要的行爲:跳過None
s或「快速失敗」。
嗨曼努埃爾!是的非常重要的部分總是如何處理無:忽略或失敗快速看到這裏的一個相關的例子:https://gist.github.com/970717 – AndreasScheinert
嗨安德烈亞斯。像你的代碼片段是我需要的。 –