Home Computer Science Building Applications with Scala
Monads are combinable parametrized container types which have support for higher-order functions. Remember higher-order functions are functions which receive functions as parameters and return functions as results. One of the most used functions in FP is Map. Map takes a function, applies it to each element in the container, and returns a new container.
Scala Map function in Scala REPL
We will see Map function in Scala REPL as follows:
Welcome to Scala 2.11.8 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_77).
Type in expressions for evaluation. Or try :help.
scala>val numbers = List(1,2,3,4,5,6) numbers: List[Int] = List(1, 2, 3, 4, 5, 6) scala>def doubleIt(i:Int):Double = i * 2
doublelt: (i: Int)Double
scala>val doubled = numbers.map( doublelt _ )
doubled: List[Double] = List(2.0, 4.0, 6.0, 8.0, 10.0, 12.0)
scala>val doubled = numbers.map( 2.0 * _ )
doubled: List[Int] = List(2.0, 4.0, 6.0, 8.0, 10.0, 12.0)
In the preceding code, we created a list of numbers containing 1,2,3,4,5, and 6. We also defined a Scala function called doublelt, which receives an integer and multiplies it by 2.0 resulting in a double number. The map function calls doublelt for each element in the List (1,2,3,4,5,6), and the result is a new container, a brand new List instance containing the new values.
Scala has some syntactical sugar which helps us to be more productive. For instance, you may realize that in the previous code, we also did - 2.0 * _. The underscore is a special operator for this specific case — it means the current value is being iterated into the collection. Scala will create a function from this expression for us.
As you might have realized, map functions are pretty useful for lots of reasons: one reason is that you can do complex computations without using the for loop explicitly, and this makes your code functional. Secondly, we can use a map function to convert element types from one type to another. That's what we did in the previous code: we transformed a list of integers into a list of doubles. Look at the following:
scala>val one = Some(1) one: Some[Int] = Some(1)
scala>val oneString = one.map(_.toString) oneString: Option[String] = Some(1)
The map function operates over several data structures and not only collections, as you can see in the previous code. You can use the map function on pretty much everything in Scala language.
The map function is great, but you can end up with nested structures. That's why, when we are working with Monads, we use a slightly different version of the map function called flatMap, which works in a very similar way to the map function, but returns the values in a flat form instead of nested values.
In order to have a monad, you need to have a method called flatMap. Other function languages such as Haskell call flatMap as bind, and use the operator >>=. The syntax changes with the language, but the concept is the same.
Monads can be built in different ways. In Scala, we need a single argument constructor which will work as a monad factory. Basically, the constructor receives one type, A, and returns Monad[A] or just M[A]. For instance, unit(A) for a List will be == List[A] and unit(A), where a is an Option == Option[A]. In Scala, you don't need to have unit; this is optional. To have a monad in Scala, you need to have map and flatMap implemented.
Working with Monads will make you write a little bit more code than before. However, you will get a way better API, which will be easier to reuse and your potential complexity will be managed, because you won't need to write a complex code full of if and for loops. The possibilities are expressed through the types, and the compiler can check it for you. Let us see a simple monad example in Scala language:
|< Prev||CONTENTS||Next >|