【问题标题】:Monad "unboxing"Monad“拆箱”
【发布时间】:2016-11-27 15:27:14
【问题描述】:

我的问题是在遵循教程Functors, Applicatives, And Monads In Pictures 及其JavaScript version 时提出的。

当文本说仿函数从上下文中解包值时,我知道 Just 5 -> 5 正在发生转换。根据 What does the "Just" syntax mean in Haskell? , Just 在 Maybe monad 的“范围内定义”。

我的问题是,整个展开过程有什么神奇之处?我的意思是,有一些语言规则会自动解开“作用域”变量的问题是什么?在我看来,此操作只是在某种表中查找,其中符号 Just 5 对应于整数 5

我的问题受到 JavaScript 版本的启发,其中 Just 5 是原型数组实例。所以拆包确实不是火箭科学。

这是一种“用于计算”的原因还是“用于程序员”的原因?为什么要在编程语言层面区分Just 55

【问题讨论】:

    标签: haskell monads functor


    【解决方案1】:

    首先,如果不了解类似 Haskell 的类型系统(即不学习 Haskell 之类的语言),我认为您无法理解 Monad 等。是的,有很多教程声称并非如此,但是在学习 Haskell 之前我已经阅读了很多教程,但我没有得到它。所以我的建议是:如果你想了解 Monad,至少学习一些 Haskell。

    对于您的问题“为什么我们在编程语言级别上区分 Just 55?”。为了类型安全。在大多数碰巧不是 Haskell 的语言中,nullnilwhatever 通常用于表示缺少值。然而,这通常会导致像 NullPointerExceptions 这样的事情,因为您没有预料到可能不存在值。

    在 Haskell 中没有 null。因此,如果您有一个Int 类型的值或其他任何值,则该值不能是null。你保证有一个价值。伟大的!但有时您实际上想要/需要对缺少的值进行编码。在 Haskell 中,我们为此使用 Maybe。所以Maybe Int 类型的东西可以是Just 5Nothing。这样一来,该值可能不存在,并且您不会意外忘记它可能是 Nothing,因为您必须显式解开该值。

    这与 Monads 没有任何关系,只是 Maybe 恰好实现了 Monad 类型类(如果你熟悉 Java,类型类有点像 Java 接口)。也就是说,Maybe 主要不是 Monad,但恰好也是 Monad。

    【讨论】:

      【解决方案2】:

      我认为您从错误的方向看这个问题。 Monad 明确不是关于展开。 Monad 是关于作文的。

      它允许您将a -> m b 类型的函数与m a 类型的值组合(不一定应用)以获得m b 类型的值。我可以理解您可能认为明显的方法是将m a 类型的值解包为a 类型的值。但很少有Monad 实例以这种方式工作。事实上,唯一能以这种方式工作的是与Identity 类型等效的那些。对于Monad 的几乎所有实例,都无法解包值。

      考虑Maybe。当起始值为Nothing 时,不可能将Maybe a 类型的值展开为a 类型的值。一元组合必须做一些比展开更有趣的事情。

      考虑[]。将 [a] 类型的值展开为 a 类型的值是不可能的,除非输入恰好是长度为 1 的列表。在其他所有情况下,一元组合所做的事情比展开更有趣。

      考虑IO。像 getLine :: IO String 这样的值不包含 String 值。解开包装是不可能的,因为它没有包装一些东西。 IO 值的一元组合不会打开任何东西。它将IO 值组合成更复杂的IO 值。

      我认为调整您对Monad 含义的看法是值得的。如果它只是一个解包接口,那将毫无用处。不过,它更微妙。这是一个合成界面。

      【讨论】:

        【解决方案3】:

        一个可能的例子是:考虑 Haskell 类型Maybe (Maybe Int)。它的值可以是以下形式

        • Nothing
        • Just Nothing
        • Just (Just n) 一些整数 n

        如果没有 Just 包装器,我们无法区分前两者。

        确实,可选类型Maybe a 的全部意义在于向现有类型a 添加一个新值(Nothing)。为了确保这样的Nothing 确实是一个新值,我们将其他值包装在Just 中。

        它还有助于类型推断。当我们看到函数调用f 'a' 时,我们可以看到f 在类型Char 上被调用,而不是在Maybe CharMaybe (Maybe Char) 类型上。类型类系统将允许f 在每种情况下都有不同的实现(这类似于某些 OOP 语言中的“重载”)。

        【讨论】:

          【解决方案4】:

          我的问题是,整个展开过程有什么神奇之处?

          这并没有什么神奇之处。您可以使用各种模式匹配(此处以 case 表达式的形式)来定义...

          mapMaybe :: (a -> b) -> Maybe a -> Maybe b
          mapMaybe f mx = case mx of
              Just x -> Just (f x)
              _ -> mx
          

          ...这与fmapMaybe 完全相同。 Functor 类唯一添加的东西 - 这是一个非常有用的东西,不要搞错 - 是一个额外的抽象级别,涵盖了可以映射的各种结构。

          为什么我们在编程语言级别上区分 Just 55

          Just 55 之间的区别更有意义的是它们之间的类型——例如在Maybe IntInt之间。如果您有x :: Int,则可以确定x 是您可以使用的Int 值。但是,如果你有mx :: Maybe Int,你就没有这样的确定性,因为Int 可能会丢失(即mx 可能是Nothing),并且类型系统会迫使你承认并处理这种可能性。

          另请参阅:jpath's answer 以了解更多关于 Maybe 有用性的 cmets(不一定与 FunctorMonad 等类相关联); Carl's answer 进一步了解 FunctorMonad 等类的有用性(Maybe 示例之外)。

          【讨论】:

            【解决方案5】:

            “展开”的含义取决于容器。 Maybe 只是一个例子。当容器是 [] 而不是 Maybe 时,“展开”意味着完全不同的东西。

            整个展开的神奇之处在于抽象:在 Monad 中,我们有一个“展开”的概念,它抽象了容器的性质;然后它开始变得“​​神奇”......

            你问Just 是什么意思:Just 只不过是 Haskell 中通过数据声明定义的 Datatype 构造函数:

             data Maybe a = Just a | Nothing
            

            Just 获取a 类型的值并创建Maybe a 类型的值。这是 Haskell 区分 a 类型值和 Maybe a 类型值的方法

            【讨论】:

              【解决方案6】:

              首先,您需要从问题中删除单子。他们没有什么可做的。把这篇文章当成是对monads的观点之一吧,可能不适合你,在haskell中能看懂monads的类型系统你可能还不太懂。

              因此,您的问题可以改写为:为什么没有隐式转换Just 5 => 5?但答案很简单。因为值Just 5 的类型为Maybe Integer,所以这个值可能是Nothing,但是在这种情况下编译器必须做什么呢?只有程序员才能解决这种情况。

              但是还有一个更让人不舒服的问题。有一些类型,例如newtype Identity a = Identity a。它只是一些价值的包装。那么,为什么没有隐式转换Identity a => a

              简单的答案是 - 尝试实现这一点将导致不同的系统类型,它不会具有当前存在的许多优良品质。据此,可以牺牲它来换取其他可能性。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2011-02-28
                • 1970-01-01
                • 1970-01-01
                • 2013-05-14
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多