`let .. in do` 和 Haskell Monads 中的 `<-` 符号有什么区别?

What is the difference between `let .. in do` and `<-` notation in Haskell Monads?

我正在尝试实现一个将字符串转换为 Maybe Ints 列表的函数,例如readInts "1 2 42 foo" = [Just 1,Just 2,Just 42,Nothing].

我的第一个方法是:

readInts (s::String) = do {
    ws <- words s;
    return (map (readMaybe::(String -> Maybe Int)) ws)
}

这导致了以下错误:

lab_monad.hs:20:52:
    Couldn't match type ‘Char’ with ‘[Char]’
    Expected type: [String]
      Actual type: String
    In the second argument of ‘map’, namely ‘ws’
    In the first argument of ‘return’, namely
      ‘(map (readMaybe :: String -> Maybe Int) ws)’
Failed, modules loaded: none.

我接下来尝试(并成功)的是:

readInts (s::String) = do {
    let ws = (words s) in do
        return (map (readMaybe::(String -> Maybe Int)) ws)
} 

我这里的问题是,words s显然是[String]类型的。为什么解释器说它是String?我对 <- 运算符有什么不了解?

ws <- words s,在列表 monad 中,非确定性地分配 onewords sws 的单词;剩下的代码只处理那个单词,return 函数 "magically" 将处理 all 个单词的结果组合到结果列表中。

readInts s = do
   ws <- words s  -- ws represents *each* word in words s
   return (readMaybe ws)

do 表示法只是使用 monadic 的语法糖 bind:

readInts s = words s >>= (\ws -> return (readMaybe ws))

不使用列表的 Monad 实例,您可以使用 map 对每个单词应用相同的功能。

readInts s = map readMaybe (words s)

另一方面,

let 只是为要在另一个表达式中使用的更复杂的表达式提供名称。它可以被认为是定义和立即应用匿名函数的语法糖。也就是说,

let x = y + z in f x

等同于

(\x -> f x) (y + z)
  ^     ^      ^
  |     |      |
  |     |      RHS of let binding
  |     part after "in"
  LHS of let binding

具有多个绑定的 let 语句等同于嵌套的 let 语句:

let x = y + z
    a = b + c
in x + a

等同于

let x = y + z
in let a = b + c
   in x + a

脱糖到

(\x -> (\a -> x + a)(b + c))(y + z)