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