Saturday 2 June 2012

Rework: Monads in Scala - 4


Monadic Zero


Do U remember second state of Response - Marvelous?
If we represent Marvelous via Answer with null inside - it's not enough, than we can translate Marvelous into Answer - ignoring it's Essentials - that is error:

    val box = new Answer[Any](null)
    val func:(Any)=>Int = (in) => 5
    val res = box map func // Produces Answer(5)

Expected behavior, that is described via The First Zero Law: Identity

     mzero map/flatMap f = mzero




Our Monadic zero wasn't enough zero to do this.

We don't try to make a difference between two Marvelous, declaration is enough, value is absent. From OOP point of view it's a Singleton, don't beget a lot of instances)
    
     case object Marvelous extends Response[Nothing](false) {
         def value = throw new NoSuchElementException("Something is wrong inside")
         def map[B](func: Nothing => B) = Marvelous
    }

This Zero should work:

    val box = Marvelous
    val res = box map {_.toString} // Returns Marvelous


Applying it twice and still getting the same

   val box = Marvelous
   val res = box map {_.toString} map {_ == Marvelous} // Returns Marvelous

Value is Nothing - try to get it returns Exception. Flag isStable set to true) Maping nothing to n
Monadic Zero comes with few Laws; The Second Zero Law: M to Zero
    
     m flatMap {x=> mzero} ≡ mzero

Plus Method


To introduce the last Monadic law, we need to refer to operation that can be done with Monads '+'.
"Add" operator for Monadic classes should mean the same as for other types:

Type Method
Int+
List:::
OptionorElse

While Response Monad is almost Option clone, it makes cense to reuse "orElse" method name. It should return alternative value if Response is invalid:

    final def orElse[B >: A](alternative: => Response[B]): Response[B] =
             if (isStable) this else alternative

I prefer to put implementation into Answer and Marvelous classes:

    final case class Answer[+A](value: A) extends Response[A](true) {
         ...
         def orElse[B >: A](alternative: => Response[B]) = this
    }

    case object Marvelous extends Response[Nothing](false) {
         ...
         def orElse[B >: Nothing](alternative: => Response[B]) = alternative
    }

Here we have a orElse method that implements plus functionality, we are ready to introduce The Third and Fourth Zero Laws: Plus


Filter

Monadic zero and plus operation are basis to new Monadic feature - Filter
Filtering is applying predicate (boolean-value function). 

    def filter(p: A=> Boolean): M[A] = ...
For example there is  a list [-1, 1, 2] and predicate function x > 0. We need to get a list from original one that has only elements which make predicate return true [1, 2]. To get this result we can present filter via flatMap:
    m flatMap {x => if(p(x)) unit(x) else mzero}
applying this returns mzero plus unit(1) plus unit (2), as U remember mzero plus something is something, then result is [1,2]
Let's implement filter function following OOP rules as well - declare in superclass and implement in children, but this is the time when one children Answer should know about another one Monadic zero:
    sealed abstract class Response[+A](val isStable: Boolean) {
        ...
        def filter(p: A => Boolean): Response[A]
   }
   final case class Answer[+A](value: A) extends Response[A](true) {
        ...
        def filter(p: A => Boolean): Response[A] = 
               if (p(value)) this else Marvelous
   }
   case object Marvelous extends Response[Nothing](false) {
      ...
      def filter(p: Nothing => Boolean): Response[Nothing] = 
               Marvelous
   }
At the end few laws:



No comments:

Post a Comment