Profunctor

class Profunctor p where
dimap :: (a -> b) -> (c -> d) -> p b c -> p a d
dimap f g = lmap f . rmap g lmap :: (a -> b) -> p b c -> p a c
lmap f = dimap f id rmap :: (b -> c) -> p a b -> p a c
rmap = dimap id

Profunctor(逆变协变函子) 是个类型类。

Profunctor类型类带两个类型参数,第一个逆变,第二个协变。

Profunctor类型类包含三个函数。

  • dimap :: (a -> b) -> (c -> d) -> p b c -> p a d

    dimap函数同时修改 Profunctor 的两个参数。
  • lmap :: (a -> b) -> p b c -> p a c

    lmap函数只修改 Profunctor 的第一个参数。
  • rmap :: (b -> c) -> p a b -> p a c

    rmap函数只修改 Profunctor 的第二个参数。

Profunctor类型类实际上将输入输出的概念一般化。

Profunctor p b c 对于输入 b 是逆变,对于输出 c 则是协变。

Profunctor 的法则

法则

dimap id id ≡ id
lmap id ≡ id
rmap id ≡ id
dimap f g ≡ lmap f . rmap g

推论

dimap (f . g) (h . i) ≡ dimap g h . dimap f i
lmap (f . g) ≡ lmap g . lmap f
rmap (f . g) ≡ rmap f . rmap g

(->) 是个Profunctor

instance Profunctor (->) where
dimap ab cd bc = cd . bc . ab
lmap = flip (.)
rmap = (.)
Prelude Data.Profunctor> lmap (+2) (*3) $ 4
18
Prelude Data.Profunctor> rmap (+3) (*3) $ 4
15
Prelude Data.Profunctor> dimap (+2) (+3) (*3) $ 4
21
  • (->) 是个典型的Profunctor,它具有输入和输出。
  • lmap (+2) (*3) $ 4 = (4+2)*3 = 18
  • rmap (+3) (*3) $ 4 = 4*3+3 = 15
  • dimap (+2) (+3) (*3) $ 4 = (4+2)*3+3 = 21
  • dimap (+2) (+3) (*3) = (+3) . (*3) . (+2)

Star 是个 Profunctor

newtype Star f d c = Star { runStar :: d -> f c }

instance Functor f => Profunctor (Star f) where
dimap ab cd (Star bfc) = Star (fmap cd . bfc . ab)
lmap k (Star f) = Star (f . k)
rmap k (Star f) = Star (fmap k . f)

Star 类型将 Functor 正向提升为 Profunctor

  • newtype Star f d c = Star { runStar :: d -> f c }

    Star 封装了一个输出为 Functor 的函数: d -> f c。
  • dimap ab cd (Star bfc) = Star (fmap cd . bfc . ab)

    dimap 函数将第一个函数 ab 应用到函数的输入端,将第二个函数 cd 应用到函数的输出端(通过fmap)。
Prelude Data.Profunctor> runStar (dimap (+1) show (Star Just)) 5
Just "6"
Prelude Data.Profunctor> runStar (dimap (+1) show (Star (\x -> [x, x+1]))) 5
["6","7"]

runStar (dimap (+1) show (Star Just)) 5 = fmap show . Just . (+1) $ 5

Costar 是个 Profunctor

newtype Costar f d c = Costar { runCostar :: f d -> c }

instance Functor f => Profunctor (Costar f) where
dimap ab cd (Costar fbc) = Costar (cd . fbc . fmap ab)
lmap k (Costar f) = Costar (f . fmap k)
rmap k (Costar f) = Costar (k . f)

Costar 类型将 Functor 反向提升为 Profunctor。

  • newtype Costar f d c = Costar { runCostar :: f d -> c }

    Costar 封装了一个输入为 Functor 的函数: f d -> c。
  • dimap ab cd (Costar fbc) = Costar (cd . fbc . fmap ab)

    dimap 函数将第一个函数 ab 应用到函数的输入端(通过fmap),将第二个函数 cd 应用到函数的输出端。
Prelude Data.Profunctor> runCostar (dimap (+1) show (Costar sum)) [1,2,3]
"9"
Prelude Data.Profunctor> runCostar (dimap (+1) show (Costar Data.Maybe.fromJust)) (Just 3)
"4"

runCostar (dimap (+1) show (Costar sum)) [1,2,3] = show . sum . fmap (+1) $ [1,2,3]

Forget 是个 Profunctor

newtype Forget r a b = Forget { runForget :: a -> r }

instance Profunctor (Forget r) where
dimap f _ (Forget k) = Forget (k . f)
lmap f (Forget k) = Forget (k . f)
rmap _ (Forget k) = Forget k

Haskell语言学习笔记(45)Profunctor的更多相关文章

  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语言学习笔记(72)Free Monad

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

  7. Haskell语言学习笔记(49)ByteString Text

    Data.ByteString String 是 [Char] 的同义词,在使用上存在List的惰性所带来的性能问题. 在处理大型二进制文件时,可以使用 ByteString 来代替 String. ...

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

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

  9. Haskell语言学习笔记(38)Lens(1)

    Lens Lens是一个接近语言级别的库,使用它可以方便的读取,设置,修改一个大的数据结构中某一部分的值. view, over, set Prelude> :m +Control.Lens P ...

随机推荐

  1. java成员内部类

    java成员内部类依赖于外部类而存在,故创建内部类需要首先创建其关联的外部类. public class Test { public static void main(String args[]) { ...

  2. dkh人力资源大数据解决方案整体架构

    大数据技术的应用正在潜移默化改变着我们的日常生活习惯和工作方式,很多看起来有点“不可思议”的事情也渐渐被我们“习以为常”.大数据可能在国内的起步较晚,但我们可能却是对大数据应用最好的了代表了.前些时候 ...

  3. BaseAction编写:免去一些重复的代码,比如继承ActionSupport和实现ModelDriven接口

    1.BaseAction package com.learning.crm.base; import java.lang.reflect.ParameterizedType; import java. ...

  4. arcgis license manager 10.2服务无法启动

    (步骤)1. 用cmd切换到 license manager 安装目录,如 C:\Program Files (x86)\ArcGIS\License10.2\bin,输入: Lmgrd -z -c ...

  5. 转 WCF中同步和异步通讯总结

    我这样分个类: WCF中, 以同步.异步角度考虑通讯的方式分为四种:跨进程同步.跨进程异步.发送队列端同步.发送队列端异步.之所以造成这样的结果源于两个因素,一个是传统概念上的同异步,一个是对于WCF ...

  6. Mybatis常见面试题 三

    1.什么是mybatis? (1)mybatis是一个优秀的基于java的持久层框架,它内部封装了jdbc,使开发者只需要关注sql语句本身,而不需要花费精力去处理加载驱动.创建连接.创建statem ...

  7. linux 守护进程(daemon process)代码-详细注释

    1. 进程组 组长不能创建新的 会话. 其它进程可以创建新的会话,创建后既成为会话首领,同时失去控制终端. 2. 会话首领可以重新打开控制终端 1 #include <stdio.h> 2 ...

  8. string截断

    public static string GetFirstString(string stringToSub, int length)         {            Regex regex ...

  9. Android自动化测试中AccessibilityService获取控件信息(2)-三种方式对比

    Android自动化测试中AccessibilityService获取控件信息(2)-三种方式对比   上一篇文章: Android自动化测试中AccessibilityService获取控件信息(1 ...

  10. signal简述

    一个几乎是最简单的应用如下: #include <unistd.h> // for alarm() #include <signal.h> // for signal() #i ...