【问题标题】:Monad "unboxing"Monad“拆箱”
【发布时间】:2016-11-27 15:27:14
【问题描述】:
【问题讨论】:
标签:
haskell
monads
functor
【解决方案1】:
首先,如果不了解类似 Haskell 的类型系统(即不学习 Haskell 之类的语言),我认为您无法理解 Monad 等。是的,有很多教程声称并非如此,但是在学习 Haskell 之前我已经阅读了很多教程,但我没有得到它。所以我的建议是:如果你想了解 Monad,至少学习一些 Haskell。
对于您的问题“为什么我们在编程语言级别上区分 Just 5 和 5?”。为了类型安全。在大多数碰巧不是 Haskell 的语言中,null、nil、whatever 通常用于表示缺少值。然而,这通常会导致像 NullPointerExceptions 这样的事情,因为您没有预料到可能不存在值。
在 Haskell 中没有 null。因此,如果您有一个Int 类型的值或其他任何值,则该值不能是null。你保证有一个价值。伟大的!但有时您实际上想要/需要对缺少的值进行编码。在 Haskell 中,我们为此使用 Maybe。所以Maybe Int 类型的东西可以是Just 5 或Nothing。这样一来,该值可能不存在,并且您不会意外忘记它可能是 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 Char 或Maybe (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
...这与fmap 的Maybe 完全相同。 Functor 类唯一添加的东西 - 这是一个非常有用的东西,不要搞错 - 是一个额外的抽象级别,涵盖了可以映射的各种结构。
为什么我们在编程语言级别上区分 Just 5 和 5?
比Just 5 和5 之间的区别更有意义的是它们之间的类型——例如在Maybe Int和Int之间。如果您有x :: Int,则可以确定x 是您可以使用的Int 值。但是,如果你有mx :: Maybe Int,你就没有这样的确定性,因为Int 可能会丢失(即mx 可能是Nothing),并且类型系统会迫使你承认并处理这种可能性。
另请参阅:jpath's answer 以了解更多关于 Maybe 有用性的 cmets(不一定与 Functor 和 Monad 等类相关联); Carl's answer 进一步了解 Functor 和 Monad 等类的有用性(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?
简单的答案是 - 尝试实现这一点将导致不同的系统类型,它不会具有当前存在的许多优良品质。据此,可以牺牲它来换取其他可能性。