blue-sky
blue-sky

Reputation: 53826

Encapsulate series of steps within a Monad

In trying to understand Monads as "a structure that represents computations defined as sequences of steps" : https://en.wikipedia.org/wiki/Monad_(functional_programming)

This example :

def optionAdd(x:Option[Int], y:Option[Int]):Option[Int] = 
  for(xx <- x; yy <- y) yield xx+yy 

taken from Scala Monad - Full worked example. This monad concatenates two Strings in a predefined order.

I'm attempting to wrap these steps within a monad :

 1. Get String to be converted to html
 2. Perform series of replace statements on the String to create html compliant String



  def getString = {
    "def test() \n { }"
  }                                               //> getString: => String
  val stringOption = Option(getString);           //> stringOption  : Option[String] = Some(def test() 
                                                  //|  { })
  def createHtmlMonad(x: Option[String]): Option[String] =
    Option(x.get.replace("\n", "<br>").replace("def", "<div style=\"color:red\" </div>"));
                                                  //> createHtmlMonad: (x: Option[String])Option[String]
  val h = createHtmlMonad(stringOption);          //> h  : Option[String] = Some(<div style="color:red" </div> test() <br> { })

The method createHtmlMonad is not a Monad ? Can this functionality be encapsulated within a Monad ?

I could just create a new method that encapsulates the series of steps but this is not a Monad :

    def getString = {
    "def test() \n { }"
  }                                               //> getString: => String

  def stepsToCreateHtml(stringOption: Option[String]) = {

    val replaceBr = Option(stringOption.get.replace("\n", "<br>"))
    val replaceStyle = Option(replaceBr.get.replace("def", "<div style=\"color:red\" </div>"))

    replaceStyle
  }                                               //> stepsToCreateHtml: (stringOption: Option[String])Option[String]
  val stringOption = Option(getString);           //> stringOption  : Option[String] = Some(def test() 
                                                  //|  { })

  val html = stepsToCreateHtml(stringOption);     //> html  : Option[String] = Some(<div style="color:red" </div> test() <br> { })
                                                  //| 

Update : I think this helps to answer the question. Instead of creating a new monad I re-use the existing Option monad :

object t extends App {

  println(parseService.getHtml("").flatMap(convertBr).flatMap(convertStyle))

  def convertBr = (str: String) => Option(str.replaceAll("\n", "<br>"))
  def convertStyle = (str: String) => Option(str.replaceAll("def", "<div style= color : \"red\"></div>"))

}

object parseService {
  def getHtml(str: String): Some[String] = {
    Some("test \n newline def")
  }
}

This is based on https://medium.com/@sinisalouc/demystifying-the-monad-in-scala-cc716bb6f534#.xprt8msoc . The monad itself is just a wrapper, the value is how it is composed with the functionality to be implemented, in this case I use the Option monad to compase appending of new Strings. I still do not fully understand monads but feel a little closer.

Upvotes: 0

Views: 80

Answers (1)

yw3410
yw3410

Reputation: 228

I find that it's easier to understand that particular analogy of a monad when it's written in it's for comprehension form,

for {
  cbr <- convertBr(parseService.getHtml("")) //step 1
  cs <- convertStyle(cbr) //step 2
} yield cs

which is compiled directly to the flatmap form which you've written in your segment. Each of the lines is a step in this case and the Option monad provides the logic of how the steps are linked together and interpreted.

In this case the Option monad terminates the next steps when the previous step does not return a value which is present, i.e. a None but allows the computation to continue when a value is returned i.e. a Some.

def flatMap[B](f: A => Option[B]): A =  this match {
    case Some(a) => f(a)
    case None => None
}

The monad then can be thought of as the computational context in which the steps happen with the flatmap determining how the computation proceeds.

It's worth noting however that this is just an analogy/metaphor. A monad is simly something that follows the monadic laws.

The law which is most important can be written in scala as simply,

val ab = for {
 a <- genA
 b <- genB
} yield (a, b)

val ba = for {
 b <- genB
 a <- genA
} yield (a, b)

assert(ab == ba)

Upvotes: 1

Related Questions