是否有任何直觉可以理解在Monad中加入两个函数?

时间:2015-09-01 04:42:56

标签: haskell functional-programming monads category-theory

joinbind一起定义,以将组合数据结构展平为单一结构。

从类型系统视图中,(+) 7 :: Num a => a -> a可以被视为Functor(+) :: Num a => a -> a -> a可以被视为Functor Functor,如何获得一些关于它的直觉而不仅仅依赖于类型系统?为什么join (+) 7 === 14

即使可以通过函数绑定过程手动步进来获得最终结果,但如果给出一些直觉,那将是很好的。

这来自NICTA练习。

-- | Binds a function on the reader ((->) t).
--
-- >>> ((*) =<< (+10)) 7
-- 119
instance Bind ((->) t) where
  (=<<) ::
    (a -> ((->) t b))
    -> ((->) t a)
    -> ((->) t b)
  (f =<< a) t =
    f (a t) t

-- | Flattens a combined structure to a single structure.
--
-- >>> join (+) 7
-- 14
join ::
  Bind f =>
  f (f a)
  -> f a
join f =
  id =<< f

*Course.State> :t join (+)
join (+) :: Num a => a -> a
*Course.State> :t join
join :: Bind f => f (f a) -> f a
*Course.State> :t (+)
(+) :: Num a => a -> a -> a

3 个答案:

答案 0 :(得分:10)

  

如何获得一些关于它的直觉而不仅仅依赖于类型系统?

我宁愿说依赖类型系统是构建特定直觉的好方法。 join的类型为:

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

专门针对(->) r,它变为:

(r -> (r -> a)) -> (r -> a)

现在让我们尝试为函数定义join

-- join :: (r -> (r -> a)) -> (r -> a)
join f = -- etc.

我们知道结果必须是r -> a函数:

join f = \x -> -- etc.

但是,我们对ra类型的内容一无所知,因此我们对f :: r -> (r -> a)x :: r一无所知。我们的无知意味着我们可以用它们做一件事:将x作为参数传递给ff x

join f = \x -> f x x

因此,函数的join两次传递相同的参数,因为这是唯一可能的实现。当然,这种实现只是一个正确的monadic join,因为它遵循monad法则:

join . fmap join = join . join
join . fmap return = id
join . return = id

验证这可能是另一项不错的练习。

答案 1 :(得分:7)

将monad的传统类比作为计算的上下文,join是一种组合上下文的方法。让我们从你的例子开始吧。 join (+) 7。使用函数作为monad意味着读者monad。 (+ 1)是一个读者monad,它接受环境并添加一个环境。因此,(+)将是读者monad中的读者monad。外部读者monad获取环境n并返回(n +)形式的读者,这将采用新环境。 join只是简单地组合了两个环境,以便您提供一次,并将给定的参数应用两次。 join (+) === \x -> (+) x x

现在,更一般地说,让我们看看其他一些例子。 Maybe monad代表潜在的失败。值Nothing是失败的计算,而Just x是成功的。 Maybe中的Maybe是一个可能失败两次的计算。值Just (Just x)显然是成功的,因此加入Just x会产生NothingJust NothingNothing表示某些时候失败,因此加入可能的失败应表明计算失败,即join

可以对列表monad进行类似的类比,其中concat仅为<>,即编写器monad,它使用monoidal运算符join来组合所讨论的输出值或任何其他monad。

join是monad的一个基本属性,它的操作使得它比functor或applicative functor强大得多。可以映射函数,应用程序可以是序列,monad可以组合。分类地,monad通常被定义为returnreturn。碰巧在Haskell中我们发现用(>>=)fmap和{{1}}来定义它更方便,但这两个定义已被证明是同义词。

答案 2 :(得分:2)

关于join的直觉是 2个容器压缩成一个容器。 .e.g

join [[1]] => [1]
join (Just (Just 1)) => 1
join (a christmas tree decorated with small cristmas tree) => a cristmas tree
等等......

现在,你如何加入职能?实际上功能,可以看作是一个容器。 例如,如果你看一个哈希表。你给了一把钥匙,你得到一个价值(或不是)。这是一个函数key -> value(或者如果你更喜欢key -> Maybe value)。 那你怎么加入2个HashMap?

假设我有(以python风格)h={"a": {"a": 1, "b": 2}, "b" : {"a" : 10, "b" : 20 }}我该如何加入它,或者如果你更喜欢它呢? 给定"a"我应该获得哪个值? h["a"]给了我{"a":1, "b":2}。我唯一可以做的就是在这个新值中再次找到“a”,这给了我1。 因此join h等于{"a":1, "b":20}

功能相同。