将一系列步骤封装在 Monad 中

Encapsulate series of steps within a Monad

试图将 Monad 理解为 "a structure that represents computations defined as sequences of steps":https://en.wikipedia.org/wiki/Monad_(functional_programming)

这个例子:

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

取自Scala Monad - Full worked example。这个 monad 按预定义的顺序连接两个字符串。

我试图将这些步骤包装在一个 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> { })

方法 createHtmlMonad 不是 Monad?这个功能可以封装在 Monad 中吗?

我可以创建一个封装一系列步骤的新方法,但这不是 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> { })
                                                  //| 

更新: 我认为这有助于回答问题。我没有创建新的 monad,而是重新使用现有的 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")
  }
}

这是基于 https://medium.com/@sinisalouc/demystifying-the-monad-in-scala-cc716bb6f534#.xprt8msoc 。 monad 本身只是一个包装器,值是它如何与要实现的功能组合在一起,在这种情况下,我使用 Option monad 来编写新字符串的附加。我仍然没有完全理解 monad,但感觉更接近了。

我发现以理解形式编写 monad 的特定类比更容易理解,

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

直接编译成您在段中编写的 flatmap 形式。在这种情况下,每一行都是一个 step 并且 Option monad 提供了 how 步骤链接在一起并解释的逻辑.

在这种情况下,Option monad 在前一步没有 return 存在的值时终止后续步骤,即 None 但允许计算继续时值是 returned 即 Some.

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

然后可以将 monad 视为 计算上下文,其中发生的步骤与 flatmap 确定计算如何进行。

但值得注意的是 这只是一个 analogy/metaphor。 monad 只是遵循 monadic laws.

的东西

最重要的法则可以简单地用scala写出来,

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

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

assert(ab == ba)