Monad 本质上不只是 "conceptual" 糖吗?

Aren't Monads essentially just "conceptual" sugar?

假设我们在 Haskell 中允许 两种 类型的函数:

将进行区分 f.x。通过将点 (.) 声明为函数名称的第一个字母,将其声明为非纯过程。

然后我们将制定规则:

有了这些语法糖和规范 - 还需要 Monad 吗?上面的规则集不允许做哪些 Monad 可以做的事情?

B/c 当我开始理解 Monads - 这正是他们的目的。只是那些非常聪明的人设法通过手头的分类理论工具集纯粹通过功能方法实现了这一目标。

没有

原则上,单子与纯度或杂质无关。 IO 可以很好地模拟不纯代码,但 Monad class 可以完美地用于像 StateMaybe 这样绝对纯的实例.

Monad 还允许以非常明确的方式表达复杂的上下文层次结构(我选择这样称呼它们)。 “pure/impure”不是您可能想要进行的唯一划分。例如,考虑 authorized/unauthorized。可能的用途列表还在继续……我鼓励您查看其他常用实例,例如 STSTMRWS、“受限 IO”和朋友更好地了解各种可能性。

很快您就会开始制作自己的 monad,根据手头的问题量身定制。

B/c as I came to understand Monads - this is exactly their purpose.

完全通用的 Monad 与 purity/impurity 或命令式排序无关。所以,如果我理解你的问题,单子肯定不是效果封装的概念糖。

考虑一下 Prelude 中绝大多数的单子:ListReaderStateContEither(->) 与效果或 IO 无关。假设 IO 是 "canonical" monad 是一个非常普遍的误解,而实际上它确实是一个退化的情况。

B/c as I came to understand Monads - this is exactly their purpose.

这篇:http://homepages.inf.ed.ac.uk/wadler/topics/monads.html#monads 是 Haskell 中关于 monad 的第一篇论文:

Category theorists invented monads in the 1960's to concisely express certain aspects of universal algebra.

所以你马上就会看到 monad 与 "pure" / "impure" 计算无关。最常见的单子(在世界上!)是 Maybe:

data Maybe a
  = Nothing
  | Just a

instance Monad Maybe where
    return = Just
    Nothing >>= f = Nothing
    Just x >>= f = f x

monad 是四元组 (Maybe, liftM, return, join),其中:

liftM :: (a -> b) -> Maybe a -> Maybe b
liftM f mb = mb >>= return . f

join :: Maybe (Maybe a) -> Maybe a
join Nothing = Nothing
join (Just mb) = mb

请注意,liftM 采用 Maybe 函数(不纯!)并将其应用于 Maybe,而 join 采用两级 Maybe 并将其简化为单层(因此结果中的 Just 来自两层 Just:

join (Just (Just x)) = Just x

而结果中的 Nothing 可以来自任一层的 Nothing:

join Nothing = Nothing
join (Just Nothing) = Nothing

)。我们可以将这些术语翻译如下:

  • Maybe:可能存在也可能不存在的值。
  • liftM:将此函数应用于值(如果存在),否则不执行任何操作。
  • return:获取这个存在的值并将其注入到 Maybe.
  • 的额外结构中
  • join:取一个可能存在也可能不存在的(可能存在也可能不存在的值)抹掉'may or may not be present'.
  • 两层的区别

现在,Maybe 是一种非常合适的数据类型。在脚本语言中,它仅通过使用 undef 或等同于表达 Nothing 的方式来表达,并以与 x 相同的方式表示 Just x。在C/C++中,使用指针类型t*来表示,并允许指针为NULL。在 Scala 中,有一个明确的容器类型: http://www.scala-lang.org/api/current/index.html#scala.Option 。所以你不能说 "oh, that's just exceptions" 因为有异常的语言仍然希望能够表达 'no value here' 而不抛出异常,然后在值存在时应用函数(这就是为什么 Scala 的 Option 类型有一个foreach 方法)。但是 'apply this function if the value is there' 正是 Maybe>>= 所做的!所以这是一个非常有用的操作。

您会注意到 C 和脚本语言通常不允许区分 NothingJust Nothing --- 值存在或不存在。在函数式语言中——比如 Haskell——允许两个版本很有趣,这就是为什么我们需要 join 来消除这种区别。 (而且,从数学上讲,用 liftMjoin 定义 >>= 比反过来更好。

顺便说一句,澄清一下关于 Haskell 和 IO 的常见误解:GHC 对 IO 的实现包装了 GHC 对 I/O 的实现的副作用。即使那是 GHC 的一个糟糕的设计决策 --- 命令式的(这与不纯的不同!)I/O 可以在系统的任何级别上进行单子建模而没有杂质。你不需要副作用(在系统的任何层)来做 I/O!