Binary operator with Option arguments

前端 未结 5 859
渐次进展
渐次进展 2021-02-09 06:49

In scala, how do I define addition over two Option arguments? Just to be specific, let\'s say they\'re wrappers for Int types (I\'m actually working with maps of do

相关标签:
5条回答
  • 2021-02-09 07:31

    Monoids

    You might find life becomes a lot easier when you realize that you can stand on the shoulders of giants and take advantage of common abstractions and the libraries built to use them. To this end, this question is basically about dealing with monoids (see related questions below for more about this) and the library in question is called scalaz.

    Using scalaz FP, this is just:

    def add(a: Option[Int], b: Option[Int]) = ~(a |+| b)
    

    What is more this works on any monoid M:

    def add[M: Monoid](a: Option[M], b: Option[M]) = ~(a |+| b)
    

    Even more usefully, it works on any number of them placed inside a Foldable container:

    def add[M: Monoid, F: Foldable](as: F[Option[M]]) = ~as.asMA.sum
    

    Note that some rather useful monoids, aside from the obvious Int, String, Boolean are:

    1. Map[A, B: Monoid]
    2. A => (B: Monoid)
    3. Option[A: Monoid]

    In fact, it's barely worth the bother of extracting your own method:

    scala> some(some(some(1))) #:: some(some(some(2))) #:: Stream.empty
    res0: scala.collection.immutable.Stream[Option[Option[Option[Int]]]] = Stream(Some(Some(Some(1))), ?)
    
    scala> ~res0.asMA.sum
    res1: Option[Option[Int]] = Some(Some(3))
    

    Some related questions

    Q. What is a monoid?

    A monoid is a type M for which there exists an associative binary operation (M, M) => M and an identity I under this operation, such that mplus(m, I) == m == mplus(I, m) for all m of type M

    Q. What is |+|?

    This is just scalaz shorthand (or ASCII madness, ymmv) for the mplus binary operation

    Q. What is ~?

    It is a unary operator meaning "or identity" which is retrofitted (using scala's implicit conversions) by the scalaz library onto Option[M] if M is a monoid. Obviously a non-empty option returns its contents; an empty option is replaced by the monoid's identity.

    Q. What is asMA.sum?

    A Foldable is basically a datastructure which can be folded over (like foldLeft, for example). Recall that foldLeft takes a seed value and an operation to compose successive computations. In the case of summing a monoid, the seed value is the identity I and the operation is mplus. You can hence call asMA.sum on a Foldable[M : Monoid]. You might need to use asMA because of the name clash with the standard library's sum method.

    Some References

    • Slides and Video of a talk I gave which gives practical examples of using monoids in the wild
    0 讨论(0)
  • 2021-02-09 07:31
    def addOpt(ao: Option[Int], bo: Option[Int]) =
        for {
            a <- ao
            b <- bo
        } yield a + b
    
    0 讨论(0)
  • 2021-02-09 07:33

    (Repeating comment above in an answer as requested)

    You don't extract the content of the option the proper way. When you match with case Some(x), x is the value inside the option(type Int) and you don't call get on that. Just do

    case Some(x) => x 
    

    Anyway, if you want content or default, a.getOrElse(0) is more convenient

    0 讨论(0)
  • 2021-02-09 07:35

    If they both default to 0 you don't need pattern matching:

      def addOpt(a:Option[Int], b:Option[Int]) = {
        a.getOrElse(0) + b.getOrElse(0)
      }
    
    0 讨论(0)
  • 2021-02-09 07:45
    def addOpts(xs: Option[Int]*) = xs.flatten.sum
    

    This will work for any number of inputs.

    0 讨论(0)
提交回复
热议问题