winterland1989 / magic-haskell

魔力haskell官方网站
293 stars 28 forks source link

Applicative ,Manod是自函子上半幺群,形式上就无法理解 #18

Open boboyada opened 7 years ago

boboyada commented 7 years ago

Monoid: (S,M,1) M是二元运算符。 (Int,+ ,0) (Int,× ,1) 都好理解。

但Applicative ,Monad是自函子上半幺群,形式上就无法理解

比如:Maybe Int 二元运算符:是什么? 单位元:又是什么? 二元运算运算对象:是什么?

<*> 运算符的运算对象就不同类型,一个是包装了函数,另一个包装了数值,单位元:又是什么?

= 运算符的运算对象就不同类型,单位元:又是什么?

winterland1989 commented 7 years ago

这句话我的书上应该是避开了的吧,没有范畴论基础的读者不大好理解,这里我来简单的帮你解释一下:

首先只有Monad是自函子上幺半群,Applicative并不是,原因在于Monad提供了join函数用来合并函子包裹(虽然默认定义了>>=,但是join可以通过>>=实现如下):

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

同时ApplicativeMonad的父类型类,所以Monad还具备一个添加函子包裹的操作:

return :: (Monad m) => a -> m a
return = pure

现在我们来看下这里自函子(endofunctor)的定义,自函子是一个范畴里的物体之间的函子映射,在Haskell的范畴上就意味着是Haskell的类型之间的相互映射,例如a -> Maybe a[a] -> [[a]]等等。这个映射过程的类别在Haskell中使用* -> *来代表,你要理解returnjoin其实都是在对m :: * -> *进行操作,而不同于只在*上操作的幺半群。我们来看下这些操作能不能构成一个幺半群:

-- 因为我们在Haskell的范畴上,我们使用了(.),实际上应该使用( ∘ )
-- 单位律
join . return == id
-- 结合律
join . (join . join) == (join . join) . join

实际上上面两条定律也是一个Monad实例必须满足的,这两条定律的实际意义就是:

x :: [[[[Int]]]]
x = [[[[1], [2,3]], [[4,5,6], [7,8,9,0]]], [[[-1], [-2,-3]], [[-4,-5,-6], [-7,-8,-9,0]]]]
join . (join . join) $ x == (join . join) . join $ x

所以我们说Monad是自函子范畴上的幺半群。切记,理解不了的时候,不要抠概念,联系实例!!!

boboyada commented 7 years ago

我先简单的理解和问一下: Monad下的半幺群, 1、其二元运算符是 ( ∘ ) 也即是针对函数的复合运算 2、其运算对象是 -> 的函数 3、pure id 就是这个复合运算符的单位元?好像不对,可能是return 函数,return函数是单位元

如果正确,我再往下慢慢啃:)

winterland1989 commented 7 years ago

首先,id(.)是可以构成幺半群的,这一点已经在base里的Endo类型上得到体现了,请认真阅读一下EndoMonoid实例声明。

其次,你的理解不正确,幺半群Monad并不是针对*的类型而言的,而是针对类别为* -> *的自函子而言的,他的操作对象是Maybe[]这样的自函子,这些自函子在值的层面的表现是千差万别的。这里的二元操作就是join,单位元就是return。你可以使用“洋葱”比喻来理解他们,join的作用是把两层洋葱合并,而return是添加一层不影响join的洋葱层,他们构成的幺半群只在“洋葱”比喻下成立。

最后,务必把握住范畴的概念,范畴是对一些物体和它们之间的关系的描述,这是把范畴和普通的集合区别开来的重要特征,我们关心的不只是这些物体,还有这些物体之间可以被组合的关系。Good luck!

boboyada commented 7 years ago

幺半群Monad并不是针对的类型而言的,而是针对类别为 -> 的自函子而言的,他的操作对象是Maybe、[]这样的自函子....... 1.其中, -> ,不就是函数的形式吗?表示需要一个入参,才能确定值的吗? 而 -> 与 任意其它的 -> *进行运算,不就是函数之间的复合? 2.感觉不对呀。 join :: Monad m => m (m a) -> m a 这是join的声明,这个声明,只有一个输入参数呀,即入参数为:m (m a) 输出是m a,从这里来看,根本就是一个 一元操作符呀,根本就不是二元操作符呀?

给您添麻烦了:)

winterland1989 commented 7 years ago

麻烦你仔细理解下: 1,范畴的概念 2,类型和类别的区别。你反复地试图用Hask范畴上的物体和映射去理解这个幺半群是没有意义的。这个幺半群只在自函子的范畴上有意义

boboyada commented 7 years ago

好吧。 假定join就是一个二元运算符,需要t1,t2两个参数。 如果向join提供了t1,于是join柯里化了,还需要一个参数才能被应用。 当t1是“类别 -> ,也是需要先提供前一个 ,才能得到下一个, 于是t2就由t1这个类别来提供了? 先留个问号吧?

关于类型与类别?确实对这两个概念之间的意图和区别有了解,书上第31章 高级类型编程,有提到, 但过于简略。能否请您在此开辟一节,提提这两个概念的区别和缘由。 因为该书从头看下去,一直看到的是类型,并且在GHCI中可以使用:t x 来测试 , 还有一个:k x的测试类别。

winterland1989 commented 7 years ago

你还是在继续试图使用Hask范畴上的物体和映射去理解,这是错误的。在Hask范畴中,物体是具体类型,映射就是函数,你脑海里的所有概念诸如参数, 函数应用都是建立在这个范畴之上的。

现在你到了自函子的范畴,请放下之前的那些概念,你要明确你此时关心的物体是自函子,映射是自函子之间的自然变幻,join之所以是二元运算是因为他可以把两层m :: * -> *合并为一层,而不是需要接受两个参数,return是单位元是因为他可以提供一层m :: * -> *,而不是因为他是一个值。

在明确范畴的概念之前,建议你停止对Monad是自函子上的幺半群这句话的研究,等到你脑海里建立起一些基本概念之后,再来看这个概念其实是非常简单的。

另外类型和类别的概念,有机会在视频里解释下吧,也欢迎你私下邮件讨论。

boboyada commented 7 years ago

以Maybe自函子为例 monid{ S: Maybe, Maybe[maybe], Maybe[Maybe[Maybe]]等等元素,这些元素,才是join的参数?

二元运算符:join 幺元:return }