trait Monad[T] { def flatMap[U](f: T => Monad[U]): Monad[U] } def unit[T](x: T): Monad[T]
flatMap
function accepts a function for input, which accepts data that is placed in a monad (the monad is a container) and returns a new monad. It is worth noting that a function can return a monad of another type (U instead of T), as will be shown later - this is a very useful thing.unit
, it is responsible for creating the monad and for each monad it is different. For example, the function unit.Option Some(x)
List List(x)
Try Success(x)
map
function and express it through the combination flatMap
and unit
. For example: def mapExample() { val monad: Option[Int] = Some(5) assert(monad.map(squareFunction) == monad.flatMap(x => Some(squareFunction(x)))) }
def squareFunction(x: Int): Option[Int] = Some(x * x) def incrementFunction(x: Int): Option[Int] = Some(x + 1)
Left unit law
and it looks like this:unit(x) flatMap f == f(x)
def leftUnitLaw() { val x = 5 val monad: Option[Int] = Some(x) val result = monad.flatMap(squareFunction) == squareFunction(x) println(result) }
true
.Right unit law
and looks like this:monad flatMap unit == monad
def rightUnitLaw() { val x = 5 val monad: Option[Int] = Some(x) val result = monad.flatMap(x => Some(x)) == monad println(result) }
x
and passes it to the function x => Some(x)
which constructs the new monad. If the monad
variable monad
assigned the value None
, the result will still be true
, because flatMap
will simply return None
and will not call the function passed to it.Associativity law
:(monad flatMap f) flatMap g == monad flatMap(x => f(x) flatMap g)
def associativityLaw() { val x = 5 val monad: Option[Int] = Some(x) val left = monad flatMap squareFunction flatMap incrementFunction val right = monad flatMap (x => squareFunction(x) flatMap incrementFunction) assert(left == right) }
for comprehension
in its usual form, that is, instead of: for (square <- for (x <- monad; sq <- squareFunction(x)) yield sq; result <- incrementFunction(square)) yield result
for (x <- monad; square <- squareFunction(x); result <- incrementFunction(square)) yield result
Future
and actors, but this is the topic of a separate article. To demonstrate the chain of calculations, we will create two simple functions for calculating the port and server host and write them to return a positive result Some
. And the creation of an InetSocketAddress
depending on the results of the work of these functions. def findPort(): Option[Int] = Some(22) def findHost(): Option[String] = Some("my.host.com") val address: Option[InetSocketAddress] = for { host <- findHost() port <- findPort() } yield new InetSocketAddress(host, port) println(address)
Some(my.host.com/82.98.86.171:22)
. Note that yield
also returns Option
to use it for further calculations. In order to get the address itself, use the map
function and display the result, if any of the functions in the chains of calculations returns None
then the total result will also be None
. address.map(add => println("Address : " + add)).getOrElse(println("Error")) // Address : my.host.com/82.98.86.171:22
flatMap
and map
will never be executed with negative input data (for Option
this is None
). The use of these functions greatly simplifies the fight against errors.Source: https://habr.com/ru/post/209510/
All Articles