Haskell 单子理论与哈斯克尔

Haskell 单子理论与哈斯克尔,haskell,types,theory,monads,Haskell,Types,Theory,Monads,大多数教程似乎给出了很多单子的例子(IO、状态、列表等等),然后期望读者能够抽象出整体原理,然后他们提到范畴理论。我不倾向于通过尝试从例子中概括来学习,我想从理论的角度理解为什么这个模式如此重要 从这条线索看, 这是一个常见的问题,我已经尝试查看了大多数建议的教程(除了Brian Beck的视频,它不会在我的linux机器上播放): 有人知道从范畴理论开始的教程,并用这些术语解释IO、state、list单子吗?以下是我不成功的尝试: 据我所知,单子由一个三元组组成:一个内函子和两个自然变换 函

大多数教程似乎给出了很多单子的例子(IO、状态、列表等等),然后期望读者能够抽象出整体原理,然后他们提到范畴理论。我不倾向于通过尝试从例子中概括来学习,我想从理论的角度理解为什么这个模式如此重要

从这条线索看, 这是一个常见的问题,我已经尝试查看了大多数建议的教程(除了Brian Beck的视频,它不会在我的linux机器上播放):

有人知道从范畴理论开始的教程,并用这些术语解释IO、state、list单子吗?以下是我不成功的尝试:

据我所知,单子由一个三元组组成:一个内函子和两个自然变换

函子通常与类型一起显示: (a->b)->(m a->m b) 我加入第二个括号只是为了强调对称性


但是,这是一个内函子,那么域和辅域不应该是这样的吗

(a->b)->(a->b)

我认为答案是域和辅域都有一种类型:

(a->b)|(MA->MB)|(MA->MB)等等

但我不确定这是否有效,或者是否符合给定函子的定义

当我们进入自然转化时,情况会变得更糟。如果我理解正确,自然变换是一个二阶函子(具有某些规则),它是从一个函子到另一个函子的函子。既然我们已经定义了函子,自然变换的一般类型是: ((a->b)->(MA->MB))->(a->b)->(MA->MB))

但我们使用的实际自然变换具有以下类型:

a->m

m a->(a->m b)->m b

这些子集是上面的一般形式吗?为什么它们是自然的转变

马丁·佩奇就是这样做的。我认为您的主要困惑在于,该类并没有真正将类型定义为函子,但它将函子从Haskell类型的类别定义为该类型的类别


按照链接符号,假设F是一个Haskell函子,这意味着有一个函子从Hask类别到F类别。

我不知道问题是什么,但是的,你是对的,Haskell中的单子被定义为三元组:

m :: * -> * -- this is endofunctor from haskell types to haskell types!
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
但范畴理论的共同定义是另一个三重定义:

m :: * -> *
return :: a -> m a
join ::  m (m a) -> m a
这有点令人困惑,但不难证明这两个定义是相等的。 要做到这一点,我们需要根据
(>>=)
(反之亦然)定义
join
。 第一步:

join :: m (m a) -> m a
join x = ?
这给了我们
x::m(ma)

对于类型为
m的东西,我们所能做的就是应用(>>=):

现在我们需要一些东西作为(>>=)的第二个参数, 从联接类型来看,我们有约束条件
(x>=y)::ma

所以这里的
y
类型将有
y::ma->ma
id::a->a
非常适合它:

join x = x >>= id
相反 其中
x::ma
y::a->mb
。 要从
x
y
获得
mb
,我们需要
a类型的东西
不幸的是,我们无法从
ma
中提取
a
。但是我们可以用它来代替其他东西(记住,monad也是一个函子):


它非常适合作为加入的论据:
(>>=)xy=join(fmap yx)
一个简短的免责声明:我对范畴理论总体上有点动摇,但我得到的印象是你至少对它有一些熟悉。希望我不会把这件事搞得太糟

有人知道从范畴理论开始的教程,并用这些术语解释IO、state、list单子吗

首先,忽略
IO
,现在,它充满了黑暗魔法。它作为命令式计算的模型工作,其原因与
State
用于建模有状态计算的原因相同,但与后者不同的是
IO
是一个黑匣子,无法从外部推断一元结构

函子通常显示为:(a->b)->(ma->mb)我加上第二个括号只是为了强调对称性


但是,这是一个内函子,那么域和辅域不应该是这样的吗

我怀疑您误解了Haskell中的类型变量与范畴理论概念的关系

首先,是的,它在Haskell类型的类别上指定了一个内函子。然而,诸如
A
之类的类型变量不属于这一类;它是一个(隐式地)在类别中所有对象上普遍量化的变量。因此,类型
(a->b)->(a->b)
只描述将每个对象映射到自身的内函式

类型构造函数描述对象上的内射函数,其中构造函数的余域元素不能用任何方式描述,除非作为类型构造函数的应用程序。即使两个类型构造函数产生同构的结果,结果类型仍然是不同的。注意,在一般情况下,类型构造函数不是函子

然后,函子签名中的类型变量
m
表示一个单参数类型构造函数。断章取义,这通常会被理解为通用量化,但在这种情况下这是不正确的,因为没有这样的函数可以存在。相反,类型类定义绑定
m
,并允许为特定类型构造函数定义此类函数

然后,结果函数表示,对于定义了
fmap
的任何类型构造函数
m
,对于任何两个对象
a
b
以及它们之间的态射,我们可以通过将
m
应用于
a
来找到给定类型之间的态射
join x = x >>= id
(>>=) :: ma -> (a -> mb) -> m b
(>>=) x y = ?
fmap :: (a -> b) -> m a -> m b
fmap y x :: m (m b)
data State s a = State (s -> (s, a))
data RealWorld = RealWorld ......
getChar :: RealWorld -> (RealWorld, Char)
Maybe_g :: Maybe b -> Maybe (Maybe c)
Maybe_g Nothing = Nothing
Maybe_g (Just a) = Just (g a)
u :: Maybe (Maybe c) -> Maybe c
u Nothing = Nothing
u (Just Nothing) = Nothing
u (Just (Just c)) = Just c
State_s_g :: (s->(s,b)) -> (s->(s,(s->(s,c))))
State_s_g p s1 = let (s2, b) = p s1 in (s2, g b)
u :: (s->(s,(s->(s,c)))) -> (s->(s,c))
u p1 s1 = let (s2, p2) = p1 s1 in p2 s2