如何理解 "m ()" 是一元计算

How to understand "m ()" is a monadic computation

来自document

when :: (Monad m) => Bool -> m () -> m ()
when p s = if p then s else return ()

when函数接受一个布尔参数和一个单元()类型的单子计算,并且仅当布尔参数为True时才执行计算。

===

作为一个 Haskell 新手,我的问题是对我来说 m () 是一些 "void" 数据,但这里文档提到它是计算。是因为Haskell的懒惰吗?

懒惰与此无关。

m/Monad 部分通常称为计算

最好的例子可能是 m = IO:

查看 putStrLn "Hello" :: IO () - 这是一个计算,当 运行 时,将在您的屏幕上打印 "Hello"

这个计算没有结果 - 所以 return 类型是 ()

现在写

hello :: Bool -> IO ()
hello sayIt =
   when sayIt (putStrLn "Hello")

然后hello True是一个计算,当运行时,将打印"Hello";而 hello False 是一种计算,当 运行 时什么都不做。


现在将其与 getLine :: IO String 进行比较 - 这是一种计算,当 运行 时,将提示您输入并将 return 输入作为 String - 这就是为什么 return 类型是 String.

这有帮助吗?

for me "m ()" is some "void" data

这有点道理,因为计算 一种特殊的数据。它与懒惰无关 - 它与上下文相关。

我们以State为例。一个类型的函数,比如 s -> () in Haskell 只能产生一个值。但是,s -> ((), s) 类型的函数是对 s 进行一些转换的常规函数​​。您遇到的问题是您只查看 () 部分,而 s -> s 部分保持隐藏状态。这就是 State 的要点 - 隐藏状态传递。

因此 State s () 可以简单地转换为 s -> ((), s) 并返回,它仍然是一个 Monad(计算),产生的值为... () .

如果我们看实际使用,现在:

(flip runState 10) $ do
    modify (+1)

这个表达式产生一个 ((), Int) 的元组; Int 部分被隐藏

它将修改状态,将状态加1。不过,它会产生 () 的中间值,这适合您的 when

when (5 > 3) $ modify (+1)

Monads 非常抽象和数学化,因此关于它们的直观陈述通常是用相当模糊的语言进行的。因此,monadic 类型的值通常被非正式地标记为 "computations," "actions" 或(不太常见)"commands" 因为这是一个有时可以帮助我们推理它们的类比。但是,当您深入挖掘时,以这种方式使用会发现这些都是空话;最终他们的意思是 "some value of a type that provides the Monad interface."

我更喜欢 "action" 这个词,所以让我继续吧。在 Haskell 中使用该词的直觉是:该语言区分 functionsactions:

  1. 函数不能有任何副作用,它们的类型类似于 a -> b
  2. 动作可能有副作用,它们的类型看起来像 IO a
    • 这样做的结果:IO () 类型的操作会产生一个无趣的结果值,因此它要么是空操作(return ()),要么是一个仅因为它而有趣的操作副作用。

Monad 然后是允许您将操作粘合在一起成为复杂操作的界面。

这一切都非常直观,但当您尝试将其应用于 IO 类型以外的许多 monad 时,这个类比就会变得相当牵强。例如,列表是一个单子:

instance Monad [] where
  return a = [a]
  as >>= f = concatMap f as

list monad 的 "actions" 或 "computations" 是……列表。列表如何成为 "action" 或 "computation"?在这种情况下类比很弱,不是吗?

所以我认为这是最好的建议:

  1. 明白"action"和"computation"是类比。没有严格的定义。
  2. 了解这些类比对于某些 monad 实例更强,而对于其他实例则较弱。
  3. 事物运作方式的最终晴雨表是 Monad 法则和与 Monad 一起使用的各种函数的定义。