Haskell的monad和范畴论的探讨

下面是上次和我一起讨论haskell的网友的继续,主要探讨了haskell的monad和范畴论的关系。

2012年3月28日:

Parker says: (10:45:06 PM)

haskell你现在有看新的东西吗

gbx says: (10:45:28 PM)

没有啊, 好久没看了

Parker says: (10:45:50 PM)

我现在看范畴论的东西

gbx says: (10:45:59 PM)

:D你强大

Parker says: (10:46:59 PM)

感觉要彻底理解monad还是要看范畴论,之前尝试过形象比喻的方式,觉得行不通。看了范畴论后,觉得好理解多了。

gbx says: (10:47:26 PM)

范畴论太难懂了

Parker says: (10:48:02 PM)

主要是抽象了些,看明白了的话也不很难

gbx says: (10:48:39 PM)

我现在觉得最难理解的是lazy eval

Parker says: (10:48:43 PM)

不过更深入的其他定理还没仔细看,有时间就多看一些

Parker says: (10:50:22 PM)

惰性求值就是按名调用,对传入的参数先不求值,在确实需要时再其求值。sicp上有一章讲这个

gbx says: (10:50:42 PM)

范畴论真的看不懂, 我看了开头几页好像懂一点。然后就进行不下去了

Parker says: (10:51:32 PM)

你是看的中文的还是英文的,我觉得英文的好看些,容易懂

gbx says: (10:51:38 PM)

sicp第一章上讲我理解,可是在haskell里这种纯lazy觉得很神奇

Parker says: (10:51:39 PM)

例子也生动

gbx says: (10:51:48 PM)

不知道翻译成C是怎样的

gbx says: (10:52:16 PM)

我看过benjiamin pierce的范畴论, 还是看不懂

Parker says: (10:52:19 PM)

惰性求值的实现细节我也不清楚

gbx says: (10:53:21 PM)

monad我模模糊糊能接受, 可以用unix上的管道来类比,lazy没啥类比物

Parker says: (10:56:24 PM)

可以惰性求值的表达式看成生成树,到最后求值时在树上的节点才会计算直

gbx says: (10:56:57 PM)

什么时候才是最后求值时?

gbx says: (10:57:39 PM)

还有monad的运算是有先后顺序的吗?

gbx says: (10:57:55 PM)

>>=前的比后面的早运算?

Parker says: (10:58:29 PM)

monad的bind是满足结合律的,一般是没有先后次序的。

gbx says: (10:59:17 PM)

没有先后顺序? 那如何保证正确?

比如有这样的式子: readFile >>= writeFile

Parker says: (10:59:35 PM)

主要看bind算子的定义是啥样的,也即是何种类型的monad。有些monad有次序

gbx says: (10:59:43 PM)

必须先read一个文件, 然后再把内容写到另外一个文件的

Parker says: (10:59:59 PM)

io monad是有次序的

gbx says: (11:00:00 PM)

如何控制有些monad有次序?

gbx says: (11:01:05 PM)

比如RWH上面举的parse的例子, 那个也是有次序的吧?要把PNM文件的所有field一个一个解析出来

Parker says: (11:01:10 PM)

关键看该monad的>>=算子的定义

gbx says: (11:01:28 PM)

parse的不是IO monad

gbx says: (11:01:34 PM)

只是分析字串罢了

gbx says: (11:02:07 PM)

它的要求必须是前面的field是对的,才计算下一个。否则fail

gbx says: (11:04:08 PM)

如果monad是有次序的,又如何来做到并行化?

Parker says: (11:05:09 PM)

支持并行化的monad类型是没有次序的

Parker says: (11:05:27 PM)

譬如list这个monad就没有

gbx says: (11:05:30 PM)

看懂那个parse的例子, 真有一种醍醐灌顶的感觉

gbx says: (11:05:48 PM)

原来parse这种丑恶的程序,可以写的这么干净

Parker says: (11:06:00 PM)

那个parse的例子,是parsec吗

gbx says: (11:06:08 PM)

不是parsec

Parker says: (11:06:16 PM)

pgm?

gbx says: (11:06:19 PM)

parse pgm文件的

gbx says: (11:06:52 PM)

parsec我粗看了一点点,觉得跟yacc有点像

gbx says: (11:07:04 PM)

我熟悉yacc,所以不觉得多惊奇

Parker says: (11:07:49 PM)

不一样的,yacc是静态处理,parsec是动态组合

gbx says: (11:08:10 PM)

从使用者的角度来看,比较像

gbx says: (11:08:24 PM)

我是说怎么来把程序写干净

Parker says: (11:09:15 PM)

parsec是动态的接水管,水管接好后,最后放水,经过不同的水管处理的水就是最后的结果

gbx says: (11:09:40 PM)

如果没有monad,就必须写:如果第一个字符不是P2,如何。。。;如果第二个字符不是w,如果。。。。。

Parker says: (11:10:42 PM)

这就是数学抽象的威力了

gbx says: (11:10:54 PM)

那个parse pgm的程序只要从头到底串起正常逻辑的code, 不需要写错误处理,真是太漂亮了

gbx says: (11:11:18 PM)

错误处理无比ugly, 可是不处理的话又不是专业的程序

gbx says: (11:11:33 PM)

monad无比干净的解决了这个问题

Parker says: (11:12:01 PM)

我写c代码的时候就很烦这些错误处理,很是丑陋,又很容易漏掉

Parker says: (11:12:12 PM)

是的

gbx says: (11:12:29 PM)

c++, java之类的异常虽然好一些, 可是也很丑陋

Parker says: (11:12:55 PM)

monad最优雅

gbx says: (11:14:39 PM)

是的,fp语言实际上最大的问题在于如果处理不纯的情况, 比如IO

gbx says: (11:14:57 PM)

像lisp, ml等都没做好

gbx says: (11:15:17 PM)

所知道的语言里只有haskell用monad处理好了

Parker says: (11:16:03 PM)

实际上是范畴论处理好的,haskell只是把范畴论的解决方法实现出来了

gbx says: (11:16:41 PM)

只有philip walder想到了

gbx says: (11:16:51 PM)

把这个理论用到语言上

Parker says: (11:18:10 PM)

范畴论最重要的概念就是morphism,即保持数学结构不变的映射,一个范畴是具有相同的数学结构的对象的聚合(Collectoin)

gbx says: (11:18:45 PM)

我懂一点群论

gbx says: (11:18:57 PM)

不过是皮毛

gbx says: (11:19:17 PM)

所谓的morphism我也能模糊的感觉到

Parker says: (11:19:18 PM)

群论在是只有一个对象的范畴

gbx says: (11:20:04 PM)

我们软件开发的分就是加一个函数映射

Parker says: (11:23:43 PM)

我看到范畴论的文章有的地方用morphism来表示一个范畴对象,很完美的将范畴递归起来。由范畴组成的范畴,functor就是范畴为对象的morphism。由此一切都统一起来了,都在递归到范畴的世界里,太简单、太美妙了。

gbx says: (11:24:54 PM)

monad是范畴间的什么关系?

Parker says: (11:25:45 PM)

软件设计现在还处于原始阶段,等到可以将软件设计的东西可以和某种数学结构对应起来时,我们就真正跨入了新的时代了,可以由数学的方式来推导设计是否优良的,而不是凭经验

Parker says: (11:26:36 PM)

monad是以functor为对象的范畴

Parker says: (11:27:08 PM)

monad也可以看成是monoid的一种泛化

gbx says: (11:27:14 PM)

既然是functor, 那么这些functor作用于什么上?

Parker says: (11:27:31 PM)

list、maybe就是functor

Parker says: (11:28:00 PM)

list可以作用在几乎任意的haskell类型上

gbx says: (11:28:20 PM)

monoid是没有单位元的群吧?

gbx says: (11:28:31 PM)

还是没有逆元的群?

gbx says: (11:28:34 PM)

我不记得了

Parker says: (11:28:50 PM)

有单位元但没有逆元

gbx says: (11:28:50 PM)

list是functor?

Parker says: (11:28:57 PM)

是的

gbx says: (11:29:12 PM)

list应该是集合吧?

Parker says: (11:29:14 PM)

也是monad

gbx says: (11:29:28 PM)

functor应该是类似于函数映射的东西吧

gbx says: (11:30:01 PM)

我们从集合,群上来讨论,我觉得list应该是集合,群。

gbx says: (11:30:18 PM)

functor这种东西应该是函数,映射之类

gbx says: (11:30:33 PM)

怎么list变成了functor?

Parker says: (11:30:36 PM)

这个你可以看看Trustno1的关于functor的介绍

gbx says: (11:31:03 PM)

:D我考虑的还是集合, 群这种低层次的抽象

Parker says: (11:31:44 PM)

list将一种haskell类型变为另一种haskell类型,因此从范畴论来看是functor

Parker says: (11:32:16 PM)

list是带参数的

gbx says: (11:32:18 PM)

list把char变成字符串, so, list就是functor?

Parker says: (11:33:22 PM)

是的,字符串是[Char]类型,即list是F:Char -> [Char]的functor

gbx says: (11:34:31 PM)

list把char变成字符串,这个跟monad的关系是什么?

Parker says: (11:35:26 PM)

实际上monad就是有更多约束的functor,在其上定义了自然变换

gbx says: (11:39:52 PM)

>>>monad是以functor为对象的范畴

那么monad这种转换是如何拥有不纯的结果呢?functor这种东西应该是纯的吧?

gbx says: (11:40:21 PM)

给functor一个input, 肯定会出来一个肯定的output

gbx says: (11:42:21 PM)

还有如何保存一些状态?就像parse pgm那样

Parker says: (11:42:22 PM)

问题在自然变换和functor的一同作用上

gbx says: (11:42:52 PM)

自然变换会引入不存的结果?

gbx says: (11:43:14 PM)

嗯, 自然变换我在看范畴论的时候看不懂是啥意思

gbx says: (11:43:21 PM)

好像就是因为这个放弃的

Parker says: (11:47:49 PM)

纯和不纯是functor的codomain的数学结构决定的

Parker says: (11:49:03 PM)

比如IO a这个类型就是不纯的根源

gbx says: (11:49:37 PM)

IO a不是functor, 而是一种另外一种范畴?

gbx says: (11:50:51 PM)

IO a到底是什么意思?是一种带有可变参数的对象?

gbx says: (11:51:14 PM)

给一个value前面加上IO, 到底代表什么呢?

Parker says: (11:51:31 PM)

而(State s) a这个类型也同样,Parse a类型其实是(State s) a类型的变形

gbx says: (11:52:22 PM)

是不是IO a是一个范畴,然后被某些functor操作,

Parker says: (11:52:32 PM)

因此Parser的>>=会有次序的区别

Parker says: (11:53:15 PM)

IO a是IO这个functor的codomain

gbx says: (11:53:43 PM)

codomain是啥

Parker says: (11:55:57 PM)

codomain称为陪域或目标

gbx says: (11:56:15 PM)

啥意思

Parker says: (11:56:38 PM)

domain称为域或源

gbx says: (11:56:50 PM)

o

gbx says: (11:56:52 PM)

我知道了

Parker says: (11:57:00 PM)

简单的说就是箭头的头和尾

gbx says: (11:57:04 PM)

domain就是函数的定义域,

gbx says: (11:57:12 PM)

codomain就是函数的值域

Parker says: (11:57:24 PM)

和函数的定义域有区别

gbx says: (11:57:27 PM)

x 属于domain, y属于codomain

gbx says: (11:57:47 PM)

:D我们拿函数来类比啊,

gbx says: (11:58:11 PM)

毕竟函数/集合中成立的东西,在范畴论也成立的

gbx says: (11:58:28 PM)

:D你要以我能听懂的语言来讲啊

Parker says: (11:59:24 PM)

可以这样比喻,但严格的推导中是不一样的

gbx says: (12:00:08 AM)

:D对于小白,不需要严格推导。只要意思明白就好了

gbx says: (12:00:21 AM)

明白原理再来严格的

gbx says: (12:01:16 AM)

so,IO跟list一样,把a变成了另外一种东西

Parker says: (12:01:31 AM)

是的

gbx says: (12:02:04 AM)

然后IO的type class的bind, return的定义里有不纯的东西

Parker says: (12:02:18 AM)

很正确

gbx says: (12:03:06 AM)

而list里的type class都是纯的

Parker says: (12:03:24 AM)

但很巧妙的是functor将纯的函数映射到了不纯的目标类型中,并正常工作

Parker says: (12:03:38 AM)

是的

gbx says: (12:03:41 AM)

这样IO, list, maybe的大的框架一样,只是return/bind不同?

Parker says: (12:04:03 AM)

就是如此

Parker says: (12:04:18 AM)

要不然如何统一到范畴论里头来

gbx says: (12:04:52 AM)

那么type class相当于范畴论里的啥

gbx says: (12:08:28 AM)

嗯,把IO/list/maybe...这些类型构造器作为一个functor, 成为一种函数映射, 这是一种很高明的想法

gbx says: (12:09:03 AM)

然后就可以把类型也看作一种变换。。。

Parker says: (12:09:35 AM)

class是定义了一种数学结构

gbx says: (12:14:03 AM)

对了,自然变换是啥意思

Parker says: (12:14:11 AM)

Haskell里面的类型就对应了范畴里面的Collection,Haskell里面的class就对应了范畴里面的morphism,把Type和Class结合到一起那么就构成了一个Instance,也就构成了一个范畴

Parker says: (12:14:24 AM)

这是Trustno1的说法,我觉得很好

gbx says: (12:15:06 AM)

Haskell里面的class是type class?

gbx says: (12:15:26 AM)

范畴里面的Collection是指什么

gbx says: (12:15:32 AM)

相当于集合?

Parker says: (12:16:46 AM)

可以这样比喻,集合有一个罗素悖论的问题

gbx says: (12:17:05 AM)

没关系,尽量用小白懂得说法

gbx says: (12:17:25 AM)

Instance是什么

Parker says: (12:18:18 AM)

是一个范畴

gbx says: (12:18:57 AM)

嗯,Collection + morphism是可以成为一个范畴了

你可能感兴趣的:(函数式编程)