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. 阅读笔记 1 火球 UML大战需求分析

    伴随着七天国庆的结束,紧张的学习生活也开始了,首先声明,阅读笔记随着我不断地阅读进度会慢慢更新,而不是一次性的写完,所以会重复的编辑.对于我选的这本   <火球 UML大战需求分析>,首先 ...

  2. [阅读笔记]Software optimization resources

    http://www.agner.org/optimize/#manuals 阅读笔记Optimizing software in C++   7. The efficiency of differe ...

  3. 《uml大战需求分析》阅读笔记05

    <uml大战需求分析>阅读笔记05 这次我主要阅读了这本书的第九十章,通过看这章的知识了解了不少的知识开发某系统的重要前提是:这个系统有谁在用?这些人通过这个系统能做什么事? 一般搞清楚这 ...

  4. <<UML大战需求分析>>阅读笔记(2)

    <<UML大战需求分析>>阅读笔记(2)> 此次读了uml大战需求分析的第三四章,我发现这本书讲的特别的好,由于这学期正在学习设计模式这本书,这本书就讲究对uml图的利用 ...

  5. uml大战需求分析阅读笔记01

    <<UML大战需求分析>>阅读笔记(1) 刚读了uml大战需求分析的第一二章,读了这些内容之后,令我深有感触.以前学习uml这门课的时候,并没有好好学,那时我认为这门课并没有什 ...

  6. Hadoop阅读笔记(七)——代理模式

    关于Hadoop已经小记了六篇,<Hadoop实战>也已经翻完7章.仔细想想,这么好的一个框架,不能只是流于应用层面,跑跑数据排序.单表链接等,想得其精髓,还需深入内部. 按照<Ha ...

  7. Hadoop阅读笔记(六)——洞悉Hadoop序列化机制Writable

    酒,是个好东西,前提要适量.今天参加了公司的年会,主题就是吃.喝.吹,除了那些天生话唠外,大部分人需要加点酒来作催化剂,让一个平时沉默寡言的码农也能成为一个喷子!在大家推杯换盏之际,难免一些画面浮现脑 ...

  8. Hadoop阅读笔记(五)——重返Hadoop目录结构

    常言道:男人是视觉动物.我觉得不完全对,我的理解是范围再扩大点,不管男人女人都是视觉动物.某些场合(比如面试.初次见面等),别人没有那么多的闲暇时间听你诉说过往以塑立一个关于你的完整模型.所以,第一眼 ...

  9. Hadoop阅读笔记(四)——一幅图看透MapReduce机制

    时至今日,已然看到第十章,似乎越是焦躁什么时候能翻完这本圣经的时候也让自己变得更加浮躁,想想后面还有一半的行程没走,我觉得这样“有口无心”的学习方式是不奏效的,或者是收效甚微的.如果有幸能有大牛路过, ...

随机推荐

  1. 【Codeforces 1106E】Lunar New Year and Red Envelopes

    [链接] 我是链接,点我呀:) [题意] 给你k个红包,每个红包可以在si..ti的时间范围内拿走. 抢完红包之后你得到wi元,然后你需要在di+1时刻才能继续抢红包 时间是线性的从1..n 然后某个 ...

  2. java中的redis工具类

    1.redis基础类 package com.qlchat.component.redis.template; import javax.annotation.PostConstruct; impor ...

  3. (44). Spring Boot日志记录SLF4J【从零开始学Spring Boot】

    在开发中打印内容,使用 System.out.println() 和 Log4j 应当是人人皆知的方法了. 其实在开发中我们不建议使用 System.out 因为大量的使用 System.out 会增 ...

  4. Master Nginx(7) - Nginx for the Developer

    Caching integration No application caching Caching in the database Caching in the filesystem Changin ...

  5. 洛谷 P1491 集合位置

    P1491 集合位置 题目描述 每次有大的活动,大家都要在一起“聚一聚”,不管是去好乐迪,还是避风塘,或者汤姆熊,大家都要玩的痛快.还记得心语和花儿在跳舞机上的激情与释放,还记得草草的投篮技艺是如此的 ...

  6. Windows超级卸载工具Total Uninstaller,能完全卸载.NET Framework

    https://github.com/tsasioglu/Total-Uninstaller

  7. 前端控制器是整个MVC框架中最为核心的一块,它主要用来拦截符合要求的外部请求,并把请求分发到不同的控制器去处理,根据控制器处理后的结果,生成相应的响应发送到客户端。前端控制器既可以使用Filter实现(Struts2采用这种方式),也可以使用Servlet来实现(spring MVC框架)。

    本文转自http://www.cnblogs.com/davidwang456/p/4090058.html 感谢作者 前端控制器是整个MVC框架中最为核心的一块,它主要用来拦截符合要求的外部请求,并 ...

  8. java package 命名空间

    原文: http://www.studytonight.com/java/package-in-java.php 创建一个简单的maven 项目的命令是: mvn  archetype:generat ...

  9. [Vue-rx] Access Events from Vue.js Templates as RxJS Streams with domStreams

    The domStreams component property enables you to access Events from your Vue.js templates as Streams ...

  10. Linux IPC之共享内存C 事例

    Linux IPC之共享内存 标签: linuxrandomnull工作 2011-08-25 11:52 4123人阅读 评论(0) 收藏 举报  分类: Linux(3)  读书札记(3)  版权 ...