有什么直觉可以理解在 Monad 中连接两个函数吗?
Is there any intuition to understand join two functions in Monad?
join
与 bind
一起定义,以将组合数据结构展平为单一结构。
从类型系统的角度来看,(+) 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
与将 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 x
。 Nothing
或 Just Nothing
表示在某一点失败,因此加入可能的失败应该表示计算失败,即 Nothing
.
列表 monad 可以做类似的类比,对于它 join
只是 concat
,writer monad,它使用幺半群运算符 <>
来组合输出值有问题,或任何其他单子。
join
是 monad 的基本 属性 并且是使它比函子或应用函子强得多的操作。 Functors 可以被映射,applicatives 可以是序列,monad 可以被组合。明确地说,monad 通常定义为 join
和 return
。恰好在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.
然而,我们对 r
和 a
类型是什么一无所知,因此我们对 f :: r -> (r -> a)
和 x :: r
也一无所知.我们的无知意味着我们只能用它们做一件事:将 x
作为参数传递给 f
和 f 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}
.
一个函数也一样
join
与 bind
一起定义,以将组合数据结构展平为单一结构。
从类型系统的角度来看,(+) 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
与将 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 x
。 Nothing
或 Just Nothing
表示在某一点失败,因此加入可能的失败应该表示计算失败,即 Nothing
.
列表 monad 可以做类似的类比,对于它 join
只是 concat
,writer monad,它使用幺半群运算符 <>
来组合输出值有问题,或任何其他单子。
join
是 monad 的基本 属性 并且是使它比函子或应用函子强得多的操作。 Functors 可以被映射,applicatives 可以是序列,monad 可以被组合。明确地说,monad 通常定义为 join
和 return
。恰好在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.
然而,我们对 r
和 a
类型是什么一无所知,因此我们对 f :: r -> (r -> a)
和 x :: r
也一无所知.我们的无知意味着我们只能用它们做一件事:将 x
作为参数传递给 f
和 f 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}
.
一个函数也一样