看着 haskell 的束缚:
Prelude> :t (>>=)
(>>=) :: Monad m => m a -> (a -> m b) -> m b
我被下面的例子搞糊涂了:
Prelude> let same x = x
Prelude> [[1]] >>= \x -> same x
[1]
看一下>>=
的签名,\x -> same x
类型如何与a -> m b
进行校验?
我本以为\x -> same x
会产生一个[b]
类型,因为这里的Monad m
类型是[]
,就我所知。
6条答案
按热度按时间xj3cbfub1#
你说
我本以为
\x -> same x
会产生一个[b]
类型,因为这里的Monad m
类型是[]
,正如我所理解的。它确实如此,因为它确实如此。
我们有
有时,
[]
描述的是一种“非决定论”效应。有时,[]
描述的是一种类似容器的数据结构。事实上,很难区分这两种目的之间的区别,这是一些人非常自豪的一个特性。我不准备同意他们的观点,但我看到了他们在做什么。imzjd6km2#
看一下
>>=
的签名,\x -> same x
类型如何与a -> m b
进行校验?实际上非常简单,看看类型签名:
因此:
因此:
这就是
Control.Monad
模块中的join
函数,对于单子,它与concat
函数相同,因此:我本以为
\x -> same x
会产生一个[b]
类型,因为这里的Monad m
类型是[]
,就我所知。确实如此,
\x -> same x
函数的类型是x -> x
,它被特殊化为[b] -> [b]
,正如我前面所解释的,因此(>>= same)
的类型是[[b]] -> [b]
,与concat
函数相同,它将一个列表的列表扁平化。concat
函数是join
函数的特殊化,它将嵌套的单子扁平化。需要注意的是,单子可以用
>>=
或fmap
和join
来定义。虽然 haskell 用
return
和>>=
函数来定义单子,但也可以用return
和另外两个运算join
和fmap
来定义单子。这个公式更符合范畴论中单子的原始定义。fmap
运算,类型为Monad m => (a -> b) -> m a -> m b
,join
运算(类型为Monad m => m (m a) -> m a
)将两层一元信息“展平”为一层。这两个公式的关系如下:
在这里,
m
具有Monad m => m a
类型,n
具有Monad m => m (m a)
类型,f
具有a -> b
类型,g
具有Monad m => a -> m b
类型,其中a
和b
是基础类型。fmap
函数是为类型和函数范畴中的任何函子定义的,而不仅仅是为单子定义的。它被期望满足函子定律:return
函数通过将值“提升”到函子中的能力来表征同一范畴中的指向函子。它应满足以下定律:另外,
join
函数表征了单子:希望能有所帮助。
vcirk6k63#
正如一些人所评论的,你在这里发现了一个关于单子的非常可爱的属性。作为参考,让我们看看bind的签名:
在你的例子中,类型
a === m b
就像你有一个[[a]]
或m (m a)
。所以,如果你重写上面的绑定操作的签名,你会得到:我提到过这很可爱,因为通过扩展,这对 * 任何 * 嵌套单子都有效。
如果你看一下get在这里应用的函数,你会发现它是恒等函数(例如,
id
,same
,:: forall a. a -> a
)。它包含在Haskell的标准库中,名为
join
。你可以在hackage上查看源代码。你会看到它被实现为bind id
、\mma -> mma >>= id
或(=<<) id
bxfogqkk4#
正如你所说,
m
是[]
,那么a
是[Integer]
(为了简单起见,忽略数字是多态的事实),b
是Integer
,所以a -> m b
变成了[Integer] -> [Integer]
。cmssoen25#
第一:我们应该使用
same
的标准版本,它被称为id
。现在,让我们重命名一些类型变量
这意味着:
id
的签名是两个类型之间的函数Map,附加的约束是两个类型必须相等。仅此而已-我们不需要任何特殊的属性,比如“平坦”。我为什么要这样写呢?如果我们也重命名绑定签名中的一些变量...
...那么我们如何插入
id
就很明显了,因为类型变量已经被相应地命名了。id
的类型等式约束a''~a
被简单地取为复合的签名,即:或者,简单地说,
所以,它把一个嵌套的单子扁平化为同一单子的一个应用,非常简单,事实上,这是一个“更基本”的操作:数学家们没有定义绑定算子,而是定义了两个态射
η :: a -> m a
(我们知道,它是return
)和μ :: m (m a) -> m a
--是的,这就是你刚刚发现的,在 haskell ,它被称为join
。b4lqfgs46#
这里的单子是
[a]
,这个例子非常复杂,下面的例子会更清楚:正如
即
>>=
是concatMap
,并且当与id
一起使用时是concat
。