From 3b6f372d51b9d0b08fff09337c3fa6d582344b7a Mon Sep 17 00:00:00 2001 From: Chris Severs Date: Thu, 7 Mar 2013 21:43:05 -0800 Subject: [PATCH 1/2] Adding foldM to Monad and a test --- .../scala/com/twitter/algebird/Monad.scala | 10 ++++++++++ .../com/twitter/algebird/MonadFoldMTest.scala | 20 +++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 algebird-test/src/test/scala/com/twitter/algebird/MonadFoldMTest.scala diff --git a/algebird-core/src/main/scala/com/twitter/algebird/Monad.scala b/algebird-core/src/main/scala/com/twitter/algebird/Monad.scala index 16fecd6e4..b4025aa93 100644 --- a/algebird-core/src/main/scala/com/twitter/algebird/Monad.scala +++ b/algebird-core/src/main/scala/com/twitter/algebird/Monad.scala @@ -19,6 +19,7 @@ import java.lang.{Integer => JInt, Short => JShort, Long => JLong, Float => JFlo import java.util.{List => JList, Map => JMap} import scala.annotation.implicitNotFound +import collection.GenTraversable /** * Simple implementation of a Monad type-class */ @@ -35,8 +36,15 @@ trait Monad[M[_]] { // flatMap(m)(apply _) == m // associativity on flatMap (you can either flatMap f first, or f to g: // flatMap(flatMap(m)(f))(g) == flatMap(m) { x => flatMap(f(x))(g) } + + def foldM[T,U](fn: (T,U) => M[T])(acc: T)(xs: GenTraversable[U]) : M[T] = + if(xs.isEmpty) + apply(acc) + else + flatMap(fn(acc,xs.head)){t: T => foldM[T,U](fn)(t)(xs.tail)} } + /** For use from Java/minimizing code bloat in scala */ abstract class AbstractMonad[M[_]] extends Monad[M] @@ -48,6 +56,8 @@ object Monad { def apply[M[_]](implicit monad: Monad[M]): Monad[M] = monad def flatMap[M[_],T,U](m: M[T])(fn: (T) => M[U])(implicit monad: Monad[M]) = monad.flatMap(m)(fn) def map[M[_],T,U](m: M[T])(fn: (T) => U)(implicit monad: Monad[M]) = monad.map(m)(fn) + def foldM[M[_],T,U](fn: (T,U)=>M[T])(acc: T)(xs: GenTraversable[U])(implicit monad: Monad[M]) = + monad.foldM(fn)(acc)(xs) // Some instances of the Monad typeclass (case for a macro): implicit val list: Monad[List] = new Monad[List] { diff --git a/algebird-test/src/test/scala/com/twitter/algebird/MonadFoldMTest.scala b/algebird-test/src/test/scala/com/twitter/algebird/MonadFoldMTest.scala new file mode 100644 index 000000000..9f6dce6e1 --- /dev/null +++ b/algebird-test/src/test/scala/com/twitter/algebird/MonadFoldMTest.scala @@ -0,0 +1,20 @@ +package com.twitter.algebird + +import org.specs._ +import Monad.option + +class MonadFoldMTest extends Specification { + noDetailedDiffs() + + "A monad foldM" should { + "fold correctly" in { + + // nice easy example from Learn You a Haskell + def binSmalls(x: Int, y: Int) : Option[Int] = if(y > 9) None else Some(x+y) + val first = option.foldM(binSmalls)(0)(List(2,8,3,1)) + first must be_==(Some(14)) + val second = option.foldM(binSmalls)(0)(List(2,11,3,1)) + second must be_==(None) + } + } +} \ No newline at end of file From 16de53a313687a6f4d8c3cfccfca6d1c6a00174b Mon Sep 17 00:00:00 2001 From: Chris Severs Date: Tue, 12 Mar 2013 13:54:01 -0700 Subject: [PATCH 2/2] Moved foldM into object and added a test case for empty list coming in --- .../main/scala/com/twitter/algebird/Monad.scala | 13 +++++-------- .../com/twitter/algebird/MonadFoldMTest.scala | 14 ++++++++++---- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/algebird-core/src/main/scala/com/twitter/algebird/Monad.scala b/algebird-core/src/main/scala/com/twitter/algebird/Monad.scala index b4025aa93..04a56f310 100644 --- a/algebird-core/src/main/scala/com/twitter/algebird/Monad.scala +++ b/algebird-core/src/main/scala/com/twitter/algebird/Monad.scala @@ -36,12 +36,6 @@ trait Monad[M[_]] { // flatMap(m)(apply _) == m // associativity on flatMap (you can either flatMap f first, or f to g: // flatMap(flatMap(m)(f))(g) == flatMap(m) { x => flatMap(f(x))(g) } - - def foldM[T,U](fn: (T,U) => M[T])(acc: T)(xs: GenTraversable[U]) : M[T] = - if(xs.isEmpty) - apply(acc) - else - flatMap(fn(acc,xs.head)){t: T => foldM[T,U](fn)(t)(xs.tail)} } @@ -56,8 +50,11 @@ object Monad { def apply[M[_]](implicit monad: Monad[M]): Monad[M] = monad def flatMap[M[_],T,U](m: M[T])(fn: (T) => M[U])(implicit monad: Monad[M]) = monad.flatMap(m)(fn) def map[M[_],T,U](m: M[T])(fn: (T) => U)(implicit monad: Monad[M]) = monad.map(m)(fn) - def foldM[M[_],T,U](fn: (T,U)=>M[T])(acc: T)(xs: GenTraversable[U])(implicit monad: Monad[M]) = - monad.foldM(fn)(acc)(xs) + def foldM[M[_],T,U](acc: T, xs: GenTraversable[U])(fn: (T,U)=>M[T])(implicit monad: Monad[M]) : M[T] = + if(xs.isEmpty) + monad.apply(acc) + else + monad.flatMap(fn(acc,xs.head)){t: T => foldM(t, xs.tail)(fn)} // Some instances of the Monad typeclass (case for a macro): implicit val list: Monad[List] = new Monad[List] { diff --git a/algebird-test/src/test/scala/com/twitter/algebird/MonadFoldMTest.scala b/algebird-test/src/test/scala/com/twitter/algebird/MonadFoldMTest.scala index 9f6dce6e1..5e7176a39 100644 --- a/algebird-test/src/test/scala/com/twitter/algebird/MonadFoldMTest.scala +++ b/algebird-test/src/test/scala/com/twitter/algebird/MonadFoldMTest.scala @@ -1,20 +1,26 @@ package com.twitter.algebird import org.specs._ -import Monad.option class MonadFoldMTest extends Specification { noDetailedDiffs() + def binSmalls(x: Int, y: Int) : Option[Int] = if(y > 9) None else Some(x+y) "A monad foldM" should { "fold correctly" in { // nice easy example from Learn You a Haskell - def binSmalls(x: Int, y: Int) : Option[Int] = if(y > 9) None else Some(x+y) - val first = option.foldM(binSmalls)(0)(List(2,8,3,1)) + + val first = Monad.foldM(0,List(2,8,3,1))(binSmalls) first must be_==(Some(14)) - val second = option.foldM(binSmalls)(0)(List(2,11,3,1)) + def binSmalls2(x: Int, y: String) : Option[Int] = if(y == "11") None else Some(x+y.toInt) + + val second = Monad.foldM(0, List("2","11","3","1"))(binSmalls2) second must be_==(None) } + "handle an empty list" in { + val third = Monad.foldM(0,List.empty)(binSmalls) + third must be_==(Some(0)) + } } } \ No newline at end of file