有什么直觉可以理解在 Monad 中连接两个函数吗?

Is there any intuition to understand join two functions in Monad?

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

从类型系统的角度来看,(+) 7 :: Num a => a -> a可以被认为是Functor(+) :: Num a => a -> a -> a可以被认为是FunctorFunctor,如何获得一些直觉而不是仅仅依赖类型系统?为什么 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

与将 monad 作为计算上下文的传统类比一样,join 是一种组合上下文的方法。让我们从你的例子开始。 join (+) 7。使用函数作为 monad 意味着 reader monad。 (+ 1) 是一个 reader monad,它获取环境并向其添加一个。因此,(+) 将是 reader monad 内的 reader monad。外部 reader monad 采用环境 n 和 returns 形式为 (n +) 的 reader,这将采用新环境。 join 简单地结合了两个环境,这样你提供一次它就会应用给定的参数两次。 join (+) === \x -> (+) x x.

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

列表 monad 可以做类似的类比,对于它 join 只是 concat,writer monad,它使用幺半群运算符 <> 来组合输出值有问题,或任何其他单子。

join 是 monad 的基本 属性 并且是使它比函子或应用函子强得多的操作。 Functors 可以被映射,applicatives 可以是序列,monad 可以被组合。明确地说,monad 通常定义为 joinreturn。恰好在Haskell中我们发现用return(>>=)fmap来定义它更方便,但这两个定义已被证明是同义的。

how to get some intuition about it instead of just relying on type system?

我宁愿说依赖类型系统是建立特定直觉的好方法。 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

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

关于 join 的直觉是 将 2 个容器压缩 为一个。 .例如

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

等...

现在,您如何加入函数?其实功能,可以看作是一个容器。 例如,如果您查看哈希 table。你给一把钥匙,你得到一个值(或没有)。它是一个函数 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}.

一个函数也一样