【问题标题】:Aren't Monads essentially just "conceptual" sugar?Monads 本质上不只是“概念”糖吗?
【发布时间】:2015-01-14 00:16:07
【问题描述】:

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

  • 严格纯(像往常一样)
  • 可能不纯(程序)

区别在于 f.x。通过将点(“.”)声明为函数名的第一个字母,将其声明为非纯过程。

我们会进一步制定规则:

  • 纯函数可以被纯函数和非纯函数调用
  • 非纯函数只能由非纯函数调用
  • :非纯函数可以强制编程

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

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

【问题讨论】:

  • 您所说的“非纯函数可以强制编程”是什么意思?
  • @DonStewart 99% 他的意思是do 符号。
  • @DonStewart:在过程中可以单独评估表达式或语句,其值存储在变量中,变量用于以下表达式或语句 - 类似
  • @BartekBanachewicz:这就是我所指的

标签: haskell monads


【解决方案1】:

没有。

原则上,Monad 与纯度或杂质无关。碰巧IO 很好地模拟了不纯代码,但是Monad 类可以完美地用于像StateMaybe 这样的实例,它们绝对是纯代码。

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

很快你就会开始制作你自己的 monad,为手头的问题量身定做。

【讨论】:

  • 我只想更加强调“不”。 Monad 允许您做的不仅仅是添加不纯函数。
  • 是的,我只是想给你一些额外的支持。 :)
  • 我想我只需要在 Monads 方面获得更深入的经验才能理解你的答案——尽管如此,也许有一个简化但具体的例子来澄清你的观点?据我所知,maybe Monad 主要只是 if 表达式的缩写,而状态是非纯度的结果——据我所知。
  • @Raffael 我可以给你一个使用纯实例之一的示例,但它实际上是从其手册中复制它。不过,我可以指出您更直接地阅读。 Three Useful Monads 解释了 Reader、Writer 和 State。来自 LYAH 的A Fistful of Monads 展示了基本原理并探索了Maybe 的使用。在阅读并理解了这些之后,您应该会发现您最初的“纯/不纯”区分非常狭窄。
  • @Raffael State 不是不纯的(它主要是 (a -> a) 函数的包装),Maybe 确实有点类似于“if”链,但在很大程度上更好的形式在它的一元外观。同样,请参阅文章以获得更详细的说明。
【解决方案2】:

B/c 我开始理解 Monads - 这正是他们的目的。

Monad 完全笼统地与纯度/杂质或命令式排序无关。所以,如果我理解你的问题,monad 肯定不是效果封装的概念糖。

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

【讨论】:

    【解决方案3】:

    B/c 我开始理解 Monads - 这正是他们的目的。

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

    范畴理论家在 1960 年代发明了单子来简明地表达通用代数的某些方面。

    所以你马上就可以看到 monad 与“纯”/“不纯”计算无关。最常见的单子(世界上!)是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:取一个可能存在或不存在的(可能存在或不存在的值),并消除“可能存在或可能不存在”两层之间的区别。

    现在,Maybe 是一个非常合适的数据类型。在脚本语言中,它仅使用undef 或等价物来表达Nothing,并以与x 相同的方式表示Just x。在 C/C++ 中,它使用指针类型t* 表示,并允许指针为NULL。在 Scala 中,有一个明确的容器类型:http://www.scala-lang.org/api/current/index.html#scala.Option。所以你不能说“哦,那只是异常”,因为有异常的语言仍然希望能够在不抛出异常的情况下表达“这里没有值”,然后在值存在时应用函数(这就是为什么 Scala 的 Option 类型有一个foreach 方法)。但是“如果值存在则应用此函数”正是Maybe>>= 所做的!所以这是一个非常有用的操作。

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

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

    【讨论】:

    • 在 C++ 中,希望我们能看到更优雅的std::optional。 C++、C 和 Java 中指针的可空性一直是愚蠢错误的根源。
    • @BartekBanachewicz 30 秒谷歌搜索表明 std::optional 实际上还没有标准化(并且它缺少 liftM>>= 的等价物,这可能是很好的补充)。
    • 哦,optional 不会是 Monad;我只是指您的指针评论。
    • 我知道,我只是想确保自己了解自己,然后再尝试澄清我的答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-16
    • 2015-09-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-09
    相关资源
    最近更新 更多