Typeclassopedia 阅读笔记:导言与 Functor
Typeclassopedia 阅读笔记
本文是对介绍 Haskell 中类型类(type classes)的文档 Typeclassopedia 的阅读笔记和简短总结,包含此文档中重要的知识点。读者请配合原文档阅读使用。
注意事项
首先,Typeclassopedia 并非介绍 Haskell 基础的新手教程,假设读者已熟练掌握 Haskell 基础知识(Haskell 初学者推荐首先阅读 Learn You a Haskell for Great Good)。
笔者接触 Haskell 时间尚短,所作总结难免有所遗漏或偏颇,欢迎及时提出指正。对知识点的描述以 Typeclassopedia 中所述为准。
导言
成为一位 Haskell 专家的关键:
- 理解 Haskell 中众多的类型。
- 熟悉众多例子,对 Haskell 中的每一个类型类以及彼此之间的关系构建起较深的直觉。
Typeclassopedia 为想要深入了解 Haskell 的人提供一个良好的起点,但读者需要经过不断地努力才可熟练掌握。
Haskell 的 type classes 结构示意图

具体解释请参考原文。
Functor
Functor,即函子,是 Haskell 中普遍存在的、最基本的类型类。你可以用以下两种方式来理解 Functor:
- 它代表某种容器,该容器能够将某一函数应用到其每一个元素上。
- 它代表某种“可计算上下文”(computational context)。
我们试着分别用以上两种方法来理解列表:
- 列表是一种容器。通过 map,我们可以将一函数 f 应用到其每一个元素上。
- 列表代表一种未确定(non-deterministic)上下文——你可以其视为一个单一值,只是该值目前尚未从其可能的值(即该列表的所有元素)中选择出来。
同样,Maybe 也可以用两种方式来理解:
- Maybe 是一种能够容纳一个元素(Just a)或没有元素(Nothing)的容器。
- Maybe 代表一种可能失败的上下文。
总结而言,第一种方式更加具体和易于理解,然而缺乏普遍性。第二种方式通用性更强,但因其抽象性,可能需要一定的时间来理解。
即便如此,依然会存在某些 Functor 用以上两种途径都难以描述。因此理解 Functor 不能单纯通过类推,而是需要依靠理解其定义、阅读大量例子来获得准确的印象。
定义
Functor 定义如下:
class Functor f where
fmap :: (a -> b) -> f a -> f b
由 f a 和 f b 我们可知,f 不是类型,而是类型构造器(type constructor),即 f 应接受另一类型作为参数并返回一个具体的类型(更精准的表达则是 f 的 kind 必须是 * -> *)。
显然是可行的,但 则错误(因为 )。
实例
上文提到的 List 和 Maybe 的 Functor 实例(instances):
instance Functor [] where
fmap _ [] = []
fmap g (x:xs) = g x : fmap g xs
-- 或者可以直接 fmap = map
instance Functor Maybe where
fmap _ Nothing = Nothing
fmap g (Just a) = Just (g a)
下面再列举一些 Functor:
原文里提到的Control.Monad.Instances 在新的版本里已被移除,这些都已作为基础设施被默认实现。
- Either e 代表可以包含两种类型元素任意之一的容器(Right 或 Left e)。它和 Maybe 较为类似,两者都可以表示不能正常获得值时的错误,但 Either e 可以保存错误的额外信息。
- ((,) e) 代表可容纳一个元素、以及对该元素的某种注释信息的容器(本质上是类型为 (e, a) 的二元组)。将其写法转换为 (e,) 更易理解(考虑类比 (1+)),这样的类型声明语法需要开启扩展 TupleSections 才可通过编译。
- ((->) e),即可接受一个类型为 e 的值作为参数的函数类型,同样也是一个 Functor。以容器的观点看,它表示一个(可能无限元素的)容器,元素类型为 a,获取元素的索引的类型为 e。更有用的方式是将其理解为一个能够以类型为 e 的值、以只读方式来查询的上下文。这也是 ((->) e) 有时被称作 read monad 的原因(详细内容将在之后讲解)。
- IO 是一种 Functor,代表可以产生出类型为 a 的值的计算,该过程中可能伴随 I/O effects(或称 side effects,副作用)。
- 除此之外,其他许多容器库中的标准类型(比如 Tree、Map 和 Sequence)都是 Functor。为数不多的反例为 Set。
Typeclassopedia 法则
已经被作为 Functor 的 instance 的类型不一定就是一个 Functor。每一个有意义的 Functor 必须遵守以下 Functor 法则:
fmap id = id
fmap (g . h) = (fmap g) . (fmap h)
- 1
- 2
该法则的目的是为了确保 fmap 函数在应用到 Functor 时不会改变该 Functor 容器的结构——或者说不会改变 Functor 的上下文。
某些通过编译器检查的 Functor instance 也可能是无效的 Functor,比如:
-- Evil Functor instance
instance Functor [] where
fmap _ [] = []
fmap g (x:xs) = g x : g x : fmap g xs
如此会导致 Functor 在大量情境下丧失其正确的语义,因此必须小心避免。
Functor 有一些很有意思的特征:
- 每一个类型至多有一个与其对应的 Functor instance(且已通过 free theorem 的证明)。
- GHC 能够为许多类型自动创建出正确的 Functor。
- 任何满足第一条 Functor 法则的类型一定满足第二条法则(在不考虑 seq 和 undefined 的前提下)。
理解
你可以用两种方式来理解 fmap。第一种方式已经提到过了:传入两个参数,一个函数和一个容器,将这个函数应用到该容器所有的元素中,提供一个新的容器——或者说将一个函数应用到该 Functor 的上下文中的值,且不改变该 Functor 的上下文。
因为 Haskell 中的函数都是科里化的(curried),fmap 的类型可写为 fmap :: (a -> b) -> (f a -> f b)。以这种形式,我们还可以将 fmap 的作用理解为转换一个普通的函数到一个可以操作容器/内容的函数 (fmap g :: f a -> f b)。这一过程被称为“提升”(lift),即将一个函数从“普通的世界”提升到“f 类型的世界”。
https://blog.csdn.net/sinat_25226993/article/details/44063429
Typeclassopedia 阅读笔记:导言与 Functor的更多相关文章
- 阅读笔记 1  火球 UML大战需求分析
		伴随着七天国庆的结束,紧张的学习生活也开始了,首先声明,阅读笔记随着我不断地阅读进度会慢慢更新,而不是一次性的写完,所以会重复的编辑.对于我选的这本 <火球 UML大战需求分析>,首先 ... 
- [阅读笔记]Software optimization resources
		http://www.agner.org/optimize/#manuals 阅读笔记Optimizing software in C++ 7. The efficiency of differe ... 
- 《uml大战需求分析》阅读笔记05
		<uml大战需求分析>阅读笔记05 这次我主要阅读了这本书的第九十章,通过看这章的知识了解了不少的知识开发某系统的重要前提是:这个系统有谁在用?这些人通过这个系统能做什么事? 一般搞清楚这 ... 
- <<UML大战需求分析>>阅读笔记(2)
		<<UML大战需求分析>>阅读笔记(2)> 此次读了uml大战需求分析的第三四章,我发现这本书讲的特别的好,由于这学期正在学习设计模式这本书,这本书就讲究对uml图的利用 ... 
- uml大战需求分析阅读笔记01
		<<UML大战需求分析>>阅读笔记(1) 刚读了uml大战需求分析的第一二章,读了这些内容之后,令我深有感触.以前学习uml这门课的时候,并没有好好学,那时我认为这门课并没有什 ... 
- Hadoop阅读笔记(七)——代理模式
		关于Hadoop已经小记了六篇,<Hadoop实战>也已经翻完7章.仔细想想,这么好的一个框架,不能只是流于应用层面,跑跑数据排序.单表链接等,想得其精髓,还需深入内部. 按照<Ha ... 
- Hadoop阅读笔记(六)——洞悉Hadoop序列化机制Writable
		酒,是个好东西,前提要适量.今天参加了公司的年会,主题就是吃.喝.吹,除了那些天生话唠外,大部分人需要加点酒来作催化剂,让一个平时沉默寡言的码农也能成为一个喷子!在大家推杯换盏之际,难免一些画面浮现脑 ... 
- Hadoop阅读笔记(五)——重返Hadoop目录结构
		常言道:男人是视觉动物.我觉得不完全对,我的理解是范围再扩大点,不管男人女人都是视觉动物.某些场合(比如面试.初次见面等),别人没有那么多的闲暇时间听你诉说过往以塑立一个关于你的完整模型.所以,第一眼 ... 
- Hadoop阅读笔记(四)——一幅图看透MapReduce机制
		时至今日,已然看到第十章,似乎越是焦躁什么时候能翻完这本圣经的时候也让自己变得更加浮躁,想想后面还有一半的行程没走,我觉得这样“有口无心”的学习方式是不奏效的,或者是收效甚微的.如果有幸能有大牛路过, ... 
随机推荐
- [bzoj2502]清理雪道[上下界网络流]
			bzoj状态里有两种,一种时间是个位数,一种是四位数,我就是四位数的那种,,,估计都是看了hzwer.. #include <bits/stdc++.h> #define INF 0x3f ... 
- CODEVS2144 砝码称重2 (哈希表)
			由于m很大,所以不能使用DP. 注意到n≤30,直接暴力2^n会TLE. 所以,将砝码平均分成两份,对一份进行一次暴力,用哈希表存下可能的结果. 对下一份再进行一次暴力,在哈希表中搜索剩余的砝码重量是 ... 
- poj 1698
			题意:爱丽丝喜欢拍电影,现在正好有N个公司找她拍电影,每部电影都指定在每周的星期几拍,要用D天拍完电影,最迟M个星期要拍完.问爱丽丝能不能拍完所有电影. 分析:350天各为一个顶点,每个顶点都有且只有 ... 
- 华为/H3C Syslog配置
			H3C交换机的设置举例1. 组网需求将系统的日志信息发送到 linux 日志主机:日志主机的IP 地址为1.2.0.1/16:信息级别高于等于 informational 的日志信息将会发送到日志主机 ... 
- 高速搞定Eclipse的语法高亮
			编辑器背景颜色 打开Preferences 选择TextEditors 语法高亮配色 这里以Javascript为例. 选择Javascript 点击右边圈出的绿色框里的选项,适当改动颜色, 高亮色參 ... 
- jsp param动作标签
			param 标签以"名字-值"对的形式为其它标签提供附加消息.这个标签与jsp:include.jsp:forward.jsp:plugin标签一起使用. param 动作标签 & ... 
- 3D数学读书笔记——多坐标系和向量基础
			本系列文章由birdlove1987编写,转载请注明出处. 文章链接: http://blog.csdn.net/zhurui_idea/article/details/24662453 第一个知识点 ... 
- js中DOMContentLoaded和load的区别
			如题:DOMContentLoaded和load都是页面加载的时候触发的事件.区别在于触发的时机不一样. 浏览器渲染页面DOM文档加载的步骤: 1.解析HTML结构. 2.加载外部脚本和css文件. ... 
- luogu1006 传纸条
			题目大意 小渊坐在矩阵的左上角,坐标 (1,1 ),小轩坐在矩阵的右下角,坐标 (m,n) .从小渊传到小轩的纸条只可以向下或者向右传递,从小轩传给小渊的纸条只可以向上或者向左传递. 在活动进行中,小 ... 
- 使用U-Boot的TFTP(远程/网络内核)
			前提条件 假设您的主机PC运行的是Ubuntu 14.04.1 LTS或更高版本,并且与您的开发平台在同一个本地网络上;为了简单起见,我们假设网络上也有DHCP服务器.如果使用Juno,请确保使用的是 ... 
