Endo Monoid

newtype Endo a = Endo { appEndo :: a -> a }
instance Monoid (Endo a) where
mempty = Endo id
Endo f `mappend` Endo g = Endo (f . g)

Endo 是个 newtype,也就是对现有类型的封装。

Endo a 封装的是一个自反射的函数,即 a->a。通过 appEndo 字段可以取出这个函数。

Endo a 在结合时结合两个函数,因此它本质上是对函数合成运算符 (.) 的封装。

Endo a 是一个幺半群。这是因为自反射函数在函数合成时满足结合律。

Dual Monoid

newtype Dual a = Dual { getDual :: a }
instance Monoid a => Monoid (Dual a) where
mempty = Dual mempty
Dual x `mappend` Dual y = Dual (y `mappend` x)

Dual 是个 newtype,也就是对现有类型的封装。

Dual a 封装的是一个值,即 a。通过 getDual 字段可以取出这个值。

Dual a 是一个幺半群,前提是 a 是个幺半群。

Dual a 在结合时交换两个操作数的值,因此它本质上是对 flip 函数的封装。

证明 Dual a 满足结合律。
(Dual x <> Dual y) <> Dual z
= Dual (y <> x) <> Dual z
= Dual (z <> (y <> x))
Dual x <> (Dual y <> Dual z)
= Dual x <> Dual (z <> y)
= Dual ((z <> y) <> x)
由于 a 是 Monoid 类型, 满足结合律
所以 (z <> y) <> x = z <> (y <> x)
=> Dual (z <> (y <> x)) = Dual ((z <> y) <> x)
=> Dual x <> (Dual y <> Dual z) = (Dual x <> Dual y) <> Dual z

Foldable 的法则

class Foldable t where
fold :: Monoid m => t m -> m
fold = foldMap id foldMap :: Monoid m => (a -> m) -> t a -> m
foldMap f = foldr (mappend . f) mempty foldr :: (a -> b -> b) -> b -> t a -> b
foldr f z t = appEndo (foldMap (Endo #. f) t) z foldl :: (b -> a -> b) -> b -> t a -> b
foldl f z t = appEndo (getDual (foldMap (Dual . Endo . flip f) t)) z

Foldable 类型类的实例都必须符合以下法则。

1. foldr f z t = appEndo (foldMap (Endo . f) t ) z
2. foldl f z t = appEndo (getDual (foldMap (Dual . Endo . flip f) t)) z
3. fold = foldMap id

[] 是个 Foldable

instance Foldable [] where
foldl = List.foldl
foldr = List.foldr
证明 Foldable 类型类的实例 [] 符合 Foldable 的法则
1. foldr f z t = appEndo (foldMap (Endo . f) t ) z
foldr f z t
= List.foldr f z [x1, x2, ..., xn]
= x1 `f` (x2 `f` (...(xn `f` z)...))
appEndo (foldMap (Endo . f) t ) z
= appEndo (foldr (mappend . (Endo . f)) mempty [x1, x2, ..., xn]) z
= appEndo (foldr (mappend . (Endo . f)) (Endo id) [x1, x2, ..., xn]) z
= appEndo ( (mappend . (Endo . f)) x1 ( (mappend . (Endo . f)) x2 ( ... ( (mappend . (Endo . f)) xn (Endo id) ) ... ) ) ) z
= appEndo ( (Endo . (x1 `f`)) <> ( (Endo . (x2 `f`)) <> ( ... <> ( (Endo . (f . id)) xn ) ... ) ) ) z
= appEndo ( Endo . ((x1 `f`) . (x2 `f`) . ... . (xn `f`)) ) $ z
= (x1 `f`) . (x2 `f`) . ... . (xn `f`) $ z
= x1 `f` (x2 `f` (...(xn `f` z)...))
2. foldl f z t = appEndo (getDual (foldMap (Dual . Endo . flip f) t)) z
foldl f z t
= List.foldl f z [x1, x2, ..., xn]
= (...((z `f` x1) `f` x2)...) `f` xn
appEndo (getDual (foldMap (Dual . Endo . flip f) t)) z
= appEndo (getDual (foldr (mappend . (Dual . Endo . flip f)) mempty [x1, x2, ..., xn])) z
= appEndo (getDual (foldr (mappend . (Dual . Endo . flip f)) (Dual . Endo . id) [x1, x2, ..., xn])) z
= appEndo ( getDual ( (Dual . Endo . flip f) x1 <> ( (Dual . Endo . flip f) x2 <> ( ... <> ( (Dual . Endo . (flip f . id)) xn ) ... ) ) ) ) z
= appEndo ( getDual ( (Dual . Endo . (`f` x1)) <> ( (Dual . Endo . (`f` x2)) <> ( ... <> ( (Dual . Endo . (`f` xn)) ) ... ) ) ) ) z
= appEndo ( getDual ( (Dual . Endo . ((`f` xn) . ... . (`f` x2) . (`f` x1)) ) ) z
= (`f` xn) . ... . (`f` x2) . (`f` x1) $ z
= (...((z `f` x1) `f` x2)...) `f` xn

build 与 (++)

build   :: forall a. (forall b. (a -> b -> b) -> b -> b) -> [a]
build g = g (:) [] (++) xs ys = foldr (:) ys xs

foldMap 与 concatMap

foldMap :: Monoid m => (a -> m) -> t a -> m

foldMap f = foldr (mappend . f) mempty

当 m := [b] 时 foldMap := concatMap

concatMap :: Foldable t => (a -> [b]) -> t a -> [b]
concatMap f xs = build (\c n -> foldr (\x b -> foldr c b (f x)) n xs)
concatMap f xs
= build (\c n -> foldr (\x b -> foldr c b (f x)) n xs)
= (\c n -> foldr (\x b -> foldr c b (f x)) n xs) (:) []
= foldr (\x b -> foldr (:) b (f x)) [] xs foldMap f xs
= foldr (mappend . f) mempty xs
= foldr ((++) . f) [] xs
= foldr (\x b -> foldr (:) b (f x)) [] xs

fold 与 concat

fold :: Monoid m => t m -> m

fold = foldMap id

当 m := [a] 时 fold := concat

concat :: Foldable t => t [a] -> [a]
concat xs = build (\c n -> foldr (\x y -> foldr c y x) n xs)
concat xs
= build (\c n -> foldr (\x y -> foldr c y x) n xs)
= (\c n -> foldr (\x y -> foldr c y x) n xs) (:) xs
= foldr (\x y -> foldr (:) y x) [] xs fold xs
= foldMap id xs
= foldr (mappend . id) mempty xs
= foldr (++) [] xs
= foldr (\x y -> foldr (:) y x) [] xs

reverse foldl foldr

用 foldl foldr 实现 reverse

How can I write reverse by foldr efficiently in Haskell?

reverse' xs
= foldl (flip (:)) [] xs
= appEndo . getDual $ foldMap (Dual . Endo . flip (flip (:))) xs $ []
= appEndo . getDual $ foldr (mappend . Dual . Endo . (:)) mempty xs $ []
= appEndo . foldr (flip mappend . Endo . (:)) mempty $ xs $ []
= foldr (flip (.) . (:)) id xs []
= flip (foldr (flip (.) . (:)) id) [] xs
reverse' = flip (foldr (flip (.) . (:)) id) []

将 reverse' 的推导过程一般化,可以得到

foldl f
= flip (foldr (flip (.) . flip f) id)

下面证明

foldl f z xs
= foldr (flip (.) . flip f) id xs z
= foldr step id xs z
where step x g a = g (f a x) step x g a = g (f a x)
step x g a = g ((flip f) x a)
step x g a = g . (flip f) x $ a
step x g = g . (flip f) x
step x g = (.) g ((flip f) x)
step x g = (flip (.)) ((flip f) x) g
step x = (flip (.)) ((flip f) x)
step x = flip (.) . flip f $ x
step = flip (.) . flip f

Haskell语言学习笔记(27)Endo, Dual, Foldable的更多相关文章

  1. Haskell语言学习笔记(88)语言扩展(1)

    ExistentialQuantification {-# LANGUAGE ExistentialQuantification #-} 存在类型专用的语言扩展 Haskell语言学习笔记(73)Ex ...

  2. Haskell语言学习笔记(79)lambda演算

    lambda演算 根据维基百科,lambda演算(英语:lambda calculus,λ-calculus)是一套从数学逻辑中发展,以变量绑定和替换的规则,来研究函数如何抽象化定义.函数如何被应用以 ...

  3. Haskell语言学习笔记(69)Yesod

    Yesod Yesod 是一个使用 Haskell 语言的 Web 框架. 安装 Yesod 首先更新 Haskell Platform 到最新版 (Yesod 依赖的库非常多,版本不一致的话很容易安 ...

  4. Haskell语言学习笔记(20)IORef, STRef

    IORef 一个在IO monad中使用变量的类型. 函数 参数 功能 newIORef 值 新建带初值的引用 readIORef 引用 读取引用的值 writeIORef 引用和值 设置引用的值 m ...

  5. Haskell语言学习笔记(39)Category

    Category class Category cat where id :: cat a a (.) :: cat b c -> cat a b -> cat a c instance ...

  6. Haskell语言学习笔记(58)Bifoldable

    Bifoldable class Bifoldable p where bifold :: Monoid m => p m m -> m bifold = bifoldMap id id ...

  7. Haskell语言学习笔记(84)Concurrent

    Control.Concurrent Prelude> import Control.Concurrent Prelude Control.Concurrent> Control.Conc ...

  8. Haskell语言学习笔记(72)Free Monad

    安装 free 包 $ cabal install free Installed free-5.0.2 Free Monad data Free f a = Pure a | Free (f (Fre ...

  9. Haskell语言学习笔记(44)Lens(2)

    自定义 Lens 和 Isos -- Some of the examples in this chapter require a few GHC extensions: -- TemplateHas ...

随机推荐

  1. linux 命令 随笔

    1 查找命令 which (寻找执行档) :这个指令是根据PATH这个环境变量所规范的路径,去搜寻执行档的档名,所以,重点是找出执行档而已,which 后面接的是完整档名,也就说执行文件 wherei ...

  2. springboot 知识点

    ---恢复内容开始--- 1springBoot项目引入方式, 1,继承自父 project (需要没有付项目才能用,一般我们的项目都会有 父 项目 所以 这种方式不推荐 ,记住有这种方式 就可以了) ...

  3. MVC ASP.NET MVC各个版本的区别 (转)

    Net Framework4.5是不支持安装在window server 2003上,如非装请用net framework4.0; MVC1.0 publsh time:2008 IDEV:VS200 ...

  4. ios之runloop笔记

    网上关于runloop的文章不计其数,再此,贴个自认为讲的比较简单明了的文章 http://www.jianshu.com/p/536184bfd163 个人理解: ios的runloop应该是类似于 ...

  5. VBA改写VBA代码

    问题源自:Excel 一个困扰我很长时间的代码转换问题-Word-ExcelHome技术论坛 -  http://club.excelhome.net/thread-1334942-1-1.html ...

  6. jenkins 定时构建 位置

    定时器构建语法 * * * * * 星号中间用空格隔开 第一个*表示分钟,取值0~59 第二个*表示小时,取值0~23 第三个*表示一个月的第几天,取值1~31 第四个*表示第几月,取值1~12 第五 ...

  7. [转][C#]验证

    文件下载 本文仅做备份,参考自:http://www.cnblogs.com/LoveJenny/p/opensource_software_license_tool__easyhelper_easy ...

  8. java接口定义和作用

    接口语法 1.接口是一种引用类型,可以等同看作类.修饰符 interface 接口名 2.接口中只能出现常量和抽象方法 3.接口其实是一个特殊的抽象类,特殊在接口是完全抽象的 4.接口中没有构造方法, ...

  9. dede:channel的type改为son,currentstyle当前样式就不起作用

    我在修改得闲佬设计作品展示列表页的时候,遇到一个问题,就是channel的type改为son时,currentstyle属性不起作用,试了好久都没办法,后来上网找资料,就找到了解决方法,记录一下.   ...

  10. 第3课 进化后的 const分析

    1.  C语言中的const (1)const修饰的变量是只读的,使得变量具有只读属性,但本质还是变量.所以不是真正的常量,它只是告诉编译器该变量不能出现在赋值符号的左边. (2)const修饰的局部 ...