下面是上次和我一起讨论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是可以成为一个范畴了