What is the reason behind the design decision in Scala that monads do not have a return/unit function in contrast to Haskell where each monad has a return function that puts a value into a standard monadic context for the given monad?
For example why List, Option, Set etc... do not have a return/unit functions defined in the standard library as shown in the slides below?
I am asking this because in the reactive Coursera course Martin Odersky explicitly mentioned this fact, as can be seen below in the slides, but did not explain why Scala does not have them even though unit/return is an essential property of a monad.
Either is one of the most useful monads in Scala. If you are wondering what a monad is, well…
In Scala, Monads is a construction which performs successive calculations. It is an object which covers the other object. It is worth noting that here, the output of an operation at some step is an input to another computations, which is a parent to the recent step of the program stated.
Scala Language Monads Monad Definition Informally, a monad is a container of elements, notated as F[_] , packed with 2 functions: flatMap (to transform this container) and unit (to create this container). Common library examples include List[T] , Set[T] and Option[T] .
In Scala we use the Unit return type to indicate "no return value." This is a "void" function. The void keyword is not used. Return An empty "return" statement can be used in a method that returns a Unit type. This means "no value."
As Ørjan Johansen said, Scala does not support method dispatching on return type. Scala object system is built over JVM one, and JVM invokevirtual
instruction, which is the main tool for dynamic polymorphism, dispatches the call based on type of this
object.
As a side note, dispatching is a process of selecting concrete method to call. In Scala/Java all methods are virtual, that is, the actual method which is called depends on actual type of the object.
class A { def hello() = println("hello method in A") } class B extends A { override def hello() = println("hello method in B") } val x: A = new A x.hello() // prints "hello method in A" val y: A = new B y.hello() // prints "hello method in B"
Here, even if y
variable is of type A
, hello
method from B
is called, because JVM "sees" that the actual type of the object in y
is B
and invokes appropriate method.
However, JVM only takes the type of the variable on which the method is called into account. It is impossible, for example, to call different methods based on runtime type of arguments without explicit checks. For example:
class A { def hello(x: Number) = println(s"Number: $x") def hello(y: Int) = println(s"Integer: $y") } val a = new A val n: Number = 10: Int a.hello(n) // prints "Number: 10"
Here we have two methods with the same name, but with different parameter type. And even if n
s actual type is Int
, hello(Number)
version is called - it is resolved statically based on n
static variable type (this feature, static resolution based on argument types, is called overloading). Hence, there is no dynamic dispatch on method arguments. Some languages support dispatching on method arguments too, for example, Common Lisp's CLOS or Clojure's multimethods work like that.
Haskell has advanced type system (it is comparable to Scala's and in fact they both originate in System F, but Scala type system supports subtyping which makes type inference much more difficult) which allows global type inference, at least, without certain extensions enabled. Haskell also has a concept of type classes, which is its tool for dynamic polymorphism. Type classes can be loosely thought of as interfaces without inheritance but with dispatch on parameter and return value types. For example, this is a valid type class:
class Read a where read :: String -> a instance Read Integer where read s = -- parse a string into an integer instance Read Double where read s = -- parse a string into a double
Then, depending on the context where method is called, read
function for Integer
or Double
can be called:
x :: Integer x = read "12345" // read for Integer is called y :: Double y = read "12345.0" // read for Double is called
This is a very powerful technique which has no correspondence in bare JVM object system, so Scala object system does not support it too. Also the lack of full-scale type inference would make this feature somewhat cumbersome to use. So, Scala standard library does not have return
/unit
method anywhere - it is impossible to express it using regular object system, there is simply no place where such a method could be defined. Consequently, monad concept in Scala is implicit and conventional - everything with appropriate flatMap
method can be considered a monad, and everything with the right methods can be used in for
construction. This is much like duck typing.
However, Scala type system together with its implicits mechanism is powerful enough to express full-featured type classes, and, by extension, generic monads in formal way, though due to difficulties in full type inference it may require adding more type annotations than in Haskell.
This is definition of monad type class in Scala:
trait Monad[M[_]] { def unit[A](a: A): M[A] def bind[A, B](ma: M[A])(f: A => M[B]): M[B] }
And this is its implementation for Option
:
implicit object OptionMonad extends Monad[Option] { def unit[A](a: A) = Some(a) def bind[A, B](ma: Option[A])(f: A => Option[B]): Option[B] = ma.flatMap(f) }
Then this can be used in generic way like this:
// note M[_]: Monad context bound // this is a port of Haskell's filterM found here: // http://hackage.haskell.org/package/base-4.7.0.1/docs/src/Control-Monad.html#filterM def filterM[M[_]: Monad, A](as: Seq[A])(f: A => M[Boolean]): M[Seq[A]] = { val m = implicitly[Monad[M]] as match { case x +: xs => m.bind(f(x)) { flg => m.bind(filterM(xs)(f)) { ys => m.unit(if (flg) x +: ys else ys) } } case _ => m.unit(Seq.empty[A]) } } // using it def run(s: Seq[Int]) = { import whatever.OptionMonad // bring type class instance into scope // leave all even numbers in the list, but fail if the list contains 13 filterM[Option, Int](s) { a => if (a == 13) None else if (a % 2 == 0) Some(true) else Some(false) } } run(1 to 16) // returns None run(16 to 32) // returns Some(List(16, 18, 20, 22, 24, 26, 28, 30, 32))
Here filterM
is written generically, for any instance of Monad
type class. Because OptionMonad
implicit object is present at filterM
call site, it will be passed to filterM
implicitly, and it will be able to make use of its methods.
You can see from above that type classes allow to emulate dispatching on return type even in Scala. In fact, this is exactly what Haskell does under the covers - both Scala and Haskell are passing a dictionary of methods implementing some type class, though in Scala it is somewhat more explicit because these "dictionaries" are first-class objects there and can be imported on demand or even passed explicitly, so it is not really a proper dispatching as it is not that embedded.
If you need this amount of genericity, you can use Scalaz library which contains a lot of type classes (including monad) and their instances for some common types, including Option
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With