尝试理解Scalaz状态单子

5

我想在我的Lift项目中开始使用scalaz。为此,我正在重新编写一些代码以符合该风格。考虑用于登录用户的代码:

  def login: CssSel = {
    var password = ""
    def submit() {
      if (doLogin) S.redirectTo("/index")
      else S.error("Wrong password")
    }
    "name=pwd"    #> SHtml.password(password, password = _) &
    "type=submit" #> SHtml.onSubmitUnit(submit)
  }

所以,这应该用状态单子重新编写。但我不知道怎么做。 尝试这样做:

val result = for {
    s       <- init[String]
    pass    <- SHtml.password(s, put(_))
    newPass <- init[String]
    res     <- "name=pwd"    #> pass &
               "type=submit" #> SHtml.onSubmit { _ =>
                 if (User.logIn("username", newPass)) S.redirectTo("/index")
                 else S.error("Wrong password")
               }
} yield (newPass, res)
result ! ""

更新:根据回答更新了示例。

有哪些关于使用ScalaZ中状态单子的好的教程/解释,展示如何使用getsput等方法?


你有没有看过我的之前的问题/答案?https://dev59.com/lGsz5IYBdhLWcg3wrJ2- - huynhjl
2
我不确定这是否真的实用。你不能只是使用一些状态单子就神奇地摆脱代码中的所有var。为了使其工作,您必须有一些链接状态更改的方法,而Lift的SHtml回调方法不支持此功能。(我认为Lift的整个概念正是您在许多闭包中隐藏变量。)尽管如此,如果能看到一个可行的示例,我会很高兴被证明是错误的。 - Debilski
2个回答

2

2

警告:我从未使用过Scala状态单子。然而,我认为我知道它表现出你所说的行为的原因。

onSubmit看到的是旧的pass,而不是我正在put的那一个

好吧,请看看您正在做什么:

... { pass =>
  ... SHtml.password(pass, _ => put(pass))
  ... User.logIn("username", pass)
}

首先,我认为你没有输入你所想要输入的内容。请尝试使用以下内容:
... SHTML.password(pass, newPass => put(newPass))

其次,我认为你没有得到你想要的东西。我不知道 Scalaz 状态单子是如何工作的,但它应该像这样:something

... User.logIn("username", get())

我认为你不应该使用pass来指代状态的变化,pass只是一个初始值,用于开始状态计算。这也解释了为什么User.logIn("username", pass)会使用“旧”的密码。
此外(尽管我不了解SHtml&的作用),我非常怀疑这样做实际上是否有效。很难解释我为什么这样认为,但这可能与SHtml在内部构建状态表达式时可能不友好有关,就像Debilski所评论的那样。SHtml.password似乎希望你提供一个任意具有副作用的函数;这种设计选择立即使它对你尝试做的事情的功能性方法不友好。

1
这是一个不完整且可能糟糕的答案。那些真正了解Lift和scalaz的人:请投票/评论以确认/否认我的结论。 - Dan Burton

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接