2015-12-21 39 views
6

在Haskell中,monads根据函数返回和绑定来定义,其中返回的类型为a -> m a,绑定的类型为m a -> (a -> m b) -> m b。之前已经指出monads can also be defined in terms of return and join,其中连接是m (m a) -> m a类型的函数。绑定可以根据连接来定义,但是可能相反吗?可以根据绑定来定义连接吗?随着单子,可以加入定义的绑定?

没有加入,我不知道我会做什么,如果我曾经以某种方式得到了一个“两次包裹”monadic值m (m a) - 没有一个函子或monad操作“删除任何图层”,可以这么说。如果这是不可能的,为什么Haskell和其他monad实现在绑定方面定义它们?它似乎严格地比基于连接的定义更有用。

+5

注“也”:如果加入不能在绑定来定义,这将_have_是Monad'的'成员(或不会为所有monad定义),而不是。 –

回答

9

这是可能的:

join :: Monad m => m (m a) -> m a 
join m = (m >>= id) 

>>=棘手的实例:

(>>=) :: m b -> (b -> m c) -> m c 
-- choosing b ~ m a , c ~ a 
(>>=) :: m (m a) -> (m a -> m a) -> m a 

,所以我们可以正确地选择id第二论据。

6

是的,它是相当简单:

join m = m >>= id 
3

绑定(>>=)做实际上是“删除层”:

(>>=) :: Monad m => m a -> (a -> m b) -> m b 

直观它“得到一些a出去了的m a的”,并以a -> m b函数然后馈送,然后产生一个单从结果中获得m b

人们通常会说它需要函数参数才能在m中重新包装其输出,但事实并非如此。它要求功能的输出为东西包装在m中,但是包装来自何处并不重要。

在实施join的情况下,我们从“双重包装”开始:m (m a)。我们可以把它插入到该签名绑定,并立即找出结合“双包”的价值时,我们可以使用的功能类型:

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

现在在这里结合将要接收的值所使用的函数那是已经包裹在m。所以我们不必“重新包装”任何东西;如果我们未经修改就返回它,它已经是输出的正确类型。有效地,这是“删除了一层包装” - 这适用于任何层,但最后一层。

这样告诉我们,我们只是要与id绑定:

join = (>>= id)