Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding Haskell's Monadic Bind Operator to Scala

In Haskell, you can use the bind operator (>>=) like this:

repli :: [a] -> [a]
repli xs = xs >>= \x -> [x,x]

*Main> repli [1,2,3]
[1,1,2,2,3,3]

I've read that flatMap is Scala's bind operator:

def repli [A](xs: List[A]): List[A] =
  xs.flatMap { x => List(x,x) }

scala> repli (List(1,2,3))
res0: List[Int] = List(1, 1, 2, 2, 3, 3)

As a pedagogic exercise, I'm trying to add support for >>= to Scala:

class MyList[T](list: List[T]) {
  def >>= [U](f: T => List[U]): List[U] = list.flatMap(f)
}
implicit def list2mylist[T](list: List[T]) = new MyList(list)

def repliNew [A](xs: List[A]): List[A] =
  xs >>= { x: A => List(x,x) }

scala> repliNew (List(1,2,3))
res1: List[Int] = List(1, 1, 2, 2, 3, 3)

This works perfectly, but only for Lists. I really want to support any class with a flatMap method. Whats the best way to go about this?

like image 956
dbyrne Avatar asked Mar 25 '11 17:03

dbyrne


2 Answers

Scalaz does it as follows:

trait MA[M[_], A] {
  def value: M[A]
  def >>=(f: A => M[B])(implicit m: Monad[M]): M[B] =
    m.bind(value, f)
}

With implicit conversions from M[A] to MA[M, A] for all M[_] and A:

implicit def ma[M[_], A](m: => M[A]): MA[M, A] = new MA[M, A] {
  lazy val value = m
}

You just need a trait Monad and an instance of it for every monad you care about:

trait Monad[M[_]] {
  def pure[A](a: => A): M[A]
  def bind[A, B](m: M[A], f: A => M[B]): M[B]
}
like image 81
Apocalisp Avatar answered Nov 15 '22 22:11

Apocalisp


What about adding a synonym for flatMap with an implicit class?

implicit class BindRich[A,B](fm:{def flatMap(a:A):B}) {
  def >>=(a:A):B = fm.flatMap(a)
}

With this in scope any object having the method flatMap will also have >>=.

like image 36
Arseniy Zhizhelev Avatar answered Nov 15 '22 23:11

Arseniy Zhizhelev