Saturday, April 9, 2011

ScalaFp 01 (Monads)






Expect frequent changes.





Computations


The idea behind the ScalaFp library is to model aspects of computations in a functional way.


Here is ComputationModule.
The abtract computation method is run.
run is Computation functionality.


Note: Module scope type parameters are called Z, Y, X ... .


Note: Other type parameters are called A, B, C ... .








package com.imaginej

package computation

trait ComputationModule {
type M[+Z] <: Computation[Z]
type In[-Z]
type Out[+Z]
trait Computation[+A] { self: M[A] =>
def run[B >: A](i: In[B]): Out[B]
}
}




Monads


It is generally accepted that monads are convenient for modeling aspects of computations.
Here is MonadModule.
The abstract monad methods are _return and flatMap.
_return is MonadModule functionality.
flatMap is Monad functionality.


Note: Module functionality names will systematically start with an underscore.


The extra concrete monad methods are _join, >=, >> and map .






package com.imaginej

package monad

import computation.ComputationModule

trait MonadModule
extends ComputationModule {
type M[+Z] <: Monad[Z]
def _return[Z]: Z => M[Z]
def _join[Z]: M[M[Z]] => M[Z] =
_.flatMap(identity)
trait Monad[+A]
extends Computation[A] { self: M[A] =>
def flatMap[B](a_f_mb: A => M[B]): M[B]
def >=[B](a_f_mb: A => M[B]): M[B] =
flatMap(a_f_mb)
def >>[B](mb: => M[B]): M[B] =
this flatMap { _ => mb }
def map[B](afb: A => B) =
this flatMap (afb andThen _return)
}
}




Monad Instances


Here are some monad instances.







IdentityMonad.






package com.imaginej

package monad

package instances

import monad.MonadModule

object IdentityMonadObject
extends MonadModule {
type M[+Z] = IdentityMonad[Z]
type In[-Z] = Unit
type Out[+Z] = Z
override def _return[Z] =
IdentityMonad[Z](_)
def _close[Z]: Z => M[Z] =
IdentityMonad[Z](_)
case class IdentityMonad[+A](val open: A)
extends Monad[A] {
override def run[B >: A](u: Unit): B =
this.open
override def flatMap[B](a_f_mb: A => M[B]) =
a_f_mb(this.open)
}
}





TraversableMonad.






package com.imaginej

package monad

package instances

import monad.MonadModule

object TraversableMonadObject
extends MonadModule {
type M[+Z] = TraversableMonad[Z]
type In[-Z] = Unit
type Out[+Z] = Traversable[Z]
override def _return[Z] =
z => TraversableMonad(Traversable(z))
def _close[Z]: Traversable[Z] => M[Z] =
TraversableMonad(_)
case class TraversableMonad[+A](val open: Traversable[A])
extends Monad[A] {
override def run[B >: A](u: Unit): Traversable[B] =
this.open
override def flatMap[B](a_f_mb: A => M[B]) =
_close(
for {
a <- this.open
b <- a_f_mb(a).open
} yield b)
}
}





OptionMonad.




Note: options are not traversable, but we make them traversable
using an implicit conversion.






package com.imaginej

package monad

package instances

import monad.MonadModule

object OptionMonadObject
extends MonadModule {
type M[+Z] = OptionMonad[Z]
type In[-Z] = Unit
type Out[+Z] = Option[Z]
override def _return[Z] =
z => OptionMonad(Some(z))
def _close[Z]: Option[Z] => M[Z] =
OptionMonad(_)
implicit def _om_f_tm[Z]: M[Z] => TraversableMonadObject.M[Z] =
mz => TraversableMonadObject._close(mz.open)
case class OptionMonad[+A](val open: Option[A])
extends Monad[A] {
override def run[B >: A](u: Unit): Option[B] =
this.open
override def flatMap[B](a_f_mb: A => M[B]): M[B] =
_close(for {
a <- this.open
b <- a_f_mb(a).open
} yield b)
}
}




Implicit Monads


It is convenient to define implicit monads for later usage.






import instances.IdentityMonadObject
import instances.TraversableMonadObject
import instances.OptionMonadObject

object MonadModule {
implicit val identityMonad =
IdentityMonadObject
implicit val traversableMonad =
TraversableMonadObject
implicit val optionMonad =
OptionMonadObject
}




Application


Here is an application.






package com.imaginej

package apps

import monad.instances._

object UsingMonads {
def usingIdentityMonad() {
import IdentityMonadObject._
val computation =
for {
x <- _close(1)
y <- _close(x + 1)
} yield (x, y)
println {
computation run ()
}
}
def usingTraversableMonad() {
import TraversableMonadObject._
val computation1 =
for {
x <- _close(List(1, 2))
y <- _close(List(x + 1, x + 2))
} yield (x, y)
println {
computation1 run ()
}
val computation2 =
for {
x <- _close(List(1, 2))
y <- _close(List())
} yield (x, y)
println {
computation2 run ()
}
}
def usingOptionMonad() {
import OptionMonadObject._
val computation1 =
for {
x <- _close(Some(1))
y <- _close(Some(x + 1))
} yield (x, y)
println {
computation1 run ()
}
val computation2 =
for {
x <- _close(Some(1))
y <- _close(None)
} yield (x, y)
println {
computation2 run ()
}
}
def usingTwoMonads() {
import TraversableMonadObject.{ _close => _closeT, _ }
import OptionMonadObject.{ _close => _closeO, _ }
val maybeInts =
_closeT(List(_closeO(Some(1)), _closeO(None), _closeO(Some(2))))
val computation =
for {
maybeInt <- maybeInts
i <- maybeInt
} yield i
println {
computation run ()
}
}
def main(args: Array[String]) {
usingIdentityMonad()
usingTraversableMonad()
usingOptionMonad()
usingTwoMonads()
}
}





Running the application yields.




$ scala com.imaginej.apps.UsingMonads
(1,2)
List((1,2), (1,3), (2,3), (2,4))
List()
Some((1,2))
None
List(1, 2)

No comments:

Post a Comment