Haskell 单子;拆箱“;

Haskell 单子;拆箱“;,haskell,monads,functor,Haskell,Monads,Functor,我的问题是在学习教程及其内容时提出的 当文本说函子从上下文中展开值时,我知道正在发生一个仅5->5转换。根据,Just是Maybemonad的“定义范围” 我的问题是,整个拆开包装的事情有什么神奇之处?我的意思是,有一些自动打开“范围”变量的语言规则有什么问题?在我看来,此操作只是某种表中的查找,其中符号Just 5对应于整数5 我的问题来自JavaScript版本,其中只有5个是原型数组实例。因此,拆开包装确实根本不是火箭科学 这是“为了计算”的原因还是“为了程序员”的原因?为什么我们要在编程

我的问题是在学习教程及其内容时提出的

当文本说函子从上下文中展开值时,我知道正在发生一个
仅5
->
5
转换。根据,Just是
Maybe
monad的“定义范围”

我的问题是,整个拆开包装的事情有什么神奇之处?我的意思是,有一些自动打开“范围”变量的语言规则有什么问题?在我看来,此操作只是某种表中的查找,其中符号
Just 5
对应于整数
5

我的问题来自JavaScript版本,其中
只有5个
是原型数组实例。因此,拆开包装确实根本不是火箭科学

这是“为了计算”的原因还是“为了程序员”的原因?为什么我们要在编程语言级别上区分
只有5
5

什么是“展开”取决于容器<代码>也许
只是一个例子。当容器是
[]
而不是
时,“展开”意味着完全不同的东西

整个拆开包装的神奇之处在于抽象:在单子中,我们有一个“拆开包装”的概念,它抽象了容器的本质;然后它开始变得“神奇”

您会问Just是什么意思:Just只是Haskell中的一个数据类型构造函数,通过如下数据声明定义:

 data Maybe a = Just a | Nothing

只需
获取类型为
a
的值,然后创建类型为
的值,可能是a
。这是哈斯凯尔区分类型
a
的高值和类型
的值的方法,也许a

首先,我认为如果不理解类似哈斯凯尔的类型系统(即不学习类似哈斯凯尔的语言),你就无法理解单子之类的东西。是的,有很多教程声称不是这样,但我在学习Haskell之前读了很多,但我没有理解。所以我的建议是:如果你想了解单子,至少要学一些哈斯克尔

对于您的问题“为什么我们要在编程语言级别上区分
5
5
?”。用于类型安全。在大多数恰好不是Haskell的语言中,
null
nil
无论什么,都经常用来表示没有值。但是,这通常会导致出现诸如
NullPointerExceptions
,因为您没有预料到可能不存在值

在Haskell中没有
null
。因此,如果您有一个类型为
Int
的值或任何其他值,则该值不能为
null
。你被保证有一个价值。伟大的但有时您实际上想要/需要对缺少值的情况进行编码。在Haskell中,我们使用
可能
。所以类型为
的东西可能是Int
可以是
只有5个
或者
什么都没有
。通过这种方式,可以明确表示该值可能不在那里,并且您不能意外地忘记它可能是
Nothing
,因为您必须显式地展开该值


这实际上与Monad无关,只是
可能碰巧实现了Monad类型类(如果您熟悉Java,类型类有点像Java接口)。这可能不是主要是Monad,但恰好也是一个单元格。

< P>一个可能的例子是:考虑Haskell类型<代码>(也许INT)< /代码>。其值可以是以下形式

  • Nothing
  • 什么都没有
  • Just(Just n)
    对于某些整数
    n
如果没有
包装器,我们就无法区分前两个

实际上,可选类型
可能是a
的全部要点是向现有类型
a
添加一个新值(
Nothing
)。为了确保这样的
Nothing
确实是一个新值,我们将其他值包装在
Just


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

我认为您从错误的方向看待这一点
Monad
显然不是关于展开的<代码>单子
是关于合成的

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

考虑一下
可能
。当起始值为
Nothing
时,不可能将
类型的值a
展开为
a
类型的值。一元构图必须做一些比拆开包装更有趣的事情

考虑
[]
。除非输入恰好是长度为1的列表,否则无法将
[a]
类型的值展开为
a
类型的值。在其他任何情况下,一元构图都比拆开包装更有趣

考虑
IO
。像
getLine::IO String
这样的值不包含
String
值。它显然是不可能打开的,因为它没有包装什么东西。
mapMaybe :: (a -> b) -> Maybe a -> Maybe b
mapMaybe f mx = case mx of
    Just x -> Just (f x)
    _ -> mx