Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>If you have <code>Option[T]</code> and if there's a <code>Monoid</code> for <code>T</code>, then there's a <code>Monoid[Option[T]]</code>:</p> <pre><code>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: =&gt;Option[T]) = (o1, o2) match { case (Some(a), Some(b)) =&gt; Some(monoid.append(a, b)) case (Some(a), _) =&gt; o1 case (_, Some(b)) =&gt; o2 case _ =&gt; zero } } </code></pre> <p>Once you are equipped with this, you can just use <code>sum</code> (better than <code>foldMap(identity)</code>, as suggested by @missingfaktor):</p> <pre><code> List(Some(1), None, Some(2), Some(3), None).asMA.sum === Some(6) </code></pre> <p><strong>UPDATE</strong></p> <p>We can actually use applicatives to simplify the code above:</p> <pre><code>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: =&gt;Option[T]) = (o1 |@| o2)(monoid.append(_, _)) } </code></pre> <p>which makes me think that we can maybe even generalize further to:</p> <pre><code>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: =&gt;F[T]) = (o1 |@| o2)(monoid.append(_, _)) } </code></pre> <p>Like that you would even be able to sum Lists of Lists, Lists of Trees,...</p> <p><strong>UPDATE2</strong></p> <p>The question clarification makes me realize that the <strong>UPDATE</strong> above is incorrect!</p> <p>First of all <code>optionTIsMonoid</code>, as refactored, is not equivalent to the first definition, since the first definition will skip <code>None</code> values while the second one will return <code>None</code> as soon as there's a <code>None</code> in the input list. But in that case, this is not a <code>Monoid</code>! Indeed, a <code>Monoid[T]</code> must respect the Monoid laws, and <code>zero</code> must be an <a href="http://en.wikipedia.org/wiki/Identity_element">identity</a> element. </p> <p>We should have:</p> <pre><code>zero |+| Some(a) = Some(a) Some(a) |+| zero = Some(a) </code></pre> <p>But when I proposed the definition for the <code>Monoid[Option[T]]</code> using the <code>Applicative</code> for <code>Option</code>, this was not the case:</p> <pre><code>None |+| Some(a) = None None |+| None = None =&gt; zero |+| a != a Some(a) |+| None = zero None |+| None = zero =&gt; a |+| zero != a </code></pre> <p>The fix is not hard, we need to change the definition of <code>zero</code>:</p> <pre><code>// 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: =&gt;Option[T]) = (o1 |@| o2)(monoid.append(_, _)) } </code></pre> <p>In this case we will have (with <code>T</code> as <code>Int</code>):</p> <pre><code>Some(0) |+| Some(i) = Some(i) Some(0) |+| None = None =&gt; zero |+| a = a Some(i) |+| Some(0) = Some(i) None |+| Some(0) = None =&gt; a |+| zero = zero </code></pre> <p>Which proves that the identity law is verified (we should also verify that the <a href="http://en.wikipedia.org/wiki/Associative">associative</a> law is respected,...).</p> <p>Now we have 2 <code>Monoid[Option[T]]</code> which we can use at will, depending on the behavior we want when summing the list: skipping <code>None</code>s or "failing fast".</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload