小智..
5
tl; dr
{-# LANGUAGE InstanceSigs #-}
newtype Id t = Id t
instance Monad Id where
return :: t -> Id t
return = Id
(=< Id b) -> Id a -> Id b
f =<< (Id x) = f x
序幕
$函数的应用运算符
forall a b. a -> b
规范定义
($) :: (a -> b) -> a -> b
f $ x = f x
infixr 0 $
就Haskell原始函数应用f x(infixl 10)而言。
组成.定义$为
(.) :: (b -> c) -> (a -> b) -> (a -> c)
f . g = \ x -> f $ g x
infixr 9 .
并满足等效条件 forall f g h.
f . id = f :: c -> d Right identity
id . g = g :: b -> c Left identity
(f . g) . h = f . (g . h) :: a -> d Associativity
.是关联的,id是左右身份。
克莱斯里三人组
在编程中,monad是函子类型构造函数,具有monad类型类的实例。定义和实现有几种等效的变体,每种变体对monad抽象的理解都略有不同。
一个仿函数是一种构造f一种* -> *与仿函数类型的类的实例。
{-# LANGUAGE KindSignatures #-}
class Functor (f :: * -> *) where
map :: (a -> b) -> (f a -> f b)
除遵循静态强制类型协议外,函子类型类的实例还必须遵守代数函子定律 forall f g.
map id = id :: f t -> f t Identity
map f . map g = map (f . g) :: f a -> f c Composition / short cut fusion
函子计算具有以下类型
forall f t. Functor f => f t
一个计算c r包括在结果 r中的上下文 c。
一元单子函数或Kleisli箭头具有以下类型
forall m a b. Functor m => a -> m b
Kleisi箭头是具有一个参数a并返回一元计算的函数m b。
Monads是按照Kleisli三元规范定义的 forall m. Functor m =>
(m, return, (=<
实现为类型类
class Functor m => Monad m where
return :: t -> m t
(=< m b) -> m a -> m b
infixr 1 =<<
所述Kleisli身份 return是Kleisli箭头促进的值t成一元上下文m。Extension或Kleisli应用程序 =< m b计算结果m a。
Kleisli组成 <=
(<= (b -> m c) -> (a -> m b) -> (a -> m c)
f <=< g = \ x -> f =<< g x
infixr 1 <=<
<=< 组成两个Kleisli箭头,将左箭头应用于右箭头应用的结果。
monad类型类的实例必须遵守monad法则,就Kleisli组成而言,其用法最为优美:forall f g h.
f <=< return = f :: c -> m d Right identity
return <=< g = g :: b -> m c Left identity
(f <=< g) <=< h = f <=< (g <=< h) :: a -> m d Associativity
<=
身分识别
身份类型
type Id t = t
是类型上的标识函数
Id :: * -> *
解释为函子,
return :: t -> Id t
= id :: t -> t
(=< Id b) -> Id a -> Id b
= ($) :: (a -> b) -> a -> b
(<= Id c) -> (a -> Id b) -> (a -> Id c)
= (.) :: (b -> c) -> (a -> b) -> (a -> c)
在规范的Haskell中,标识monad被定义
newtype Id t = Id t
instance Functor Id where
map :: (a -> b) -> Id a -> Id b
map f (Id x) = Id (f x)
instance Monad Id where
return :: t -> Id t
return = Id
(=< Id b) -> Id a -> Id b
f =<< (Id x) = f x
选项
选项类型
data Maybe t = Nothing | Just t
Maybe t对不一定会产生结果的t计算进行编码,即可能“失败”的计算。选项monad已定义
instance Functor Maybe where
map :: (a -> b) -> (Maybe a -> Maybe b)
map f (Just x) = Just (f x)
map _ Nothing = Nothing
instance Monad Maybe where
return :: t -> Maybe t
return = Just
(=< Maybe b) -> Maybe a -> Maybe b
f =<< (Just x) = f x
_ =<< Nothing = Nothing
a -> Maybe b仅当Maybe a产生结果时才应用于结果。
newtype Nat = Nat Int
可以将自然数编码为大于或等于零的那些整数。
toNat :: Int -> Maybe Nat
toNat i | i >= 0 = Just (Nat i)
| otherwise = Nothing
减法不会关闭自然数。
(-?) :: Nat -> Nat -> Maybe Nat
(Nat n) -? (Nat m) = toNat (n - m)
infixl 6 -?
选项monad涵盖了异常处理的基本形式。
(-? 20) <=< toNat :: Int -> Maybe Nat
清单
列表单子,在列表类型之上
data [] t = [] | t : [t]
infixr 5 :
并且其附加的monoid操作“附加”
(++) :: [t] -> [t] -> [t]
(x : xs) ++ ys = x : xs ++ ys
[] ++ ys = ys
infixr 5 ++
对非线性计算进行编码[t]产生自然0, 1, ...的结果t。
instance Functor [] where
map :: (a -> b) -> ([a] -> [b])
map f (x : xs) = f x : map f xs
map _ [] = []
instance Monad [] where
return :: t -> [t]
return = (: [])
(=< [b]) -> [a] -> [b]
f =<< (x : xs) = f x ++ (f =<< xs)
_ =<< [] = []
扩展将将Kleisli箭头应用于的元素所得到的所有列表=< [b][a][b]
让一个正整数的正确除数n是
divisors :: Integral t => t -> [t]
divisors n = filter (`divides` n) [2 .. n - 1]
divides :: Integral t => t -> t -> Bool
(`divides` n) = (== 0) . (n `rem`)
然后
forall n. let { f = f <=< divisors } in f n = []
在定义monad类型类而不是扩展时=<>=。
class Applicative m => Monad m where
(>>=) :: forall a b. m a -> (a -> m b) -> m b
(>>) :: forall a b. m a -> m b -> m b
m >> k = m >>= \ _ -> k
{-# INLINE (>>) #-}
return :: a -> m a
return = pure
为简单起见,此说明使用类型类层次结构
class Functor f
class Functor m => Monad m
在Haskell中,当前的标准层次结构为
class Functor f
class Functor p => Applicative p
class Applicative m => Monad m
因为不仅每个monad都是函子,而且每个应用程序都是函子,每个monad也都是应用程序。
使用列表monad,命令伪代码
for a in (1, ..., 10)
for b in (1, ..., 10)
p
if even(p)
yield p
大致翻译为do块,
do a
b
let p = a * b
guard (even p)
return p
等效的monad理解,
[ p | a
和表达
[1 .. 10] >>= (\ a ->
[1 .. 10] >>= (\ b ->
let p = a * b in
guard (even p) >> -- [ () | even p ] >>
return p
)
)
注解和monad理解是嵌套绑定表达式的语法糖。bind操作符用于单子结果的本地名称绑定。
let x = v in e = (\ x -> e) $ v = v & (\ x -> e)
do { r c) =<< m = m >>= (\ r -> c)
哪里
(&) :: a -> (a -> b) -> b
(&) = flip ($)
infixl 0 &
保护功能已定义
guard :: Additive m => Bool -> m ()
guard True = return ()
guard False = fail
其中单元类型或“空的元组”
data () = ()
可以使用类型类抽象化支持选择和失败的加法蒙纳德
class Monad m => Additive m where
fail :: m t
() :: m t -> m t -> m t
infixl 3
instance Additive Maybe where
fail = Nothing
Nothing m = m
m _ = m
instance Additive [] where
fail = []
() = (++)
其中fail并形成一幺forall k l m.
k fail = k
fail l = l
(k l) m = k (l m)
并且fail是添加单核的吸收//灭零元素
_ =<< fail = fail
如果在
guard (even p) >> return p
even p为真,则警卫队产生[()],并根据的定义>>,产生局部常数
\ _ -> return p
适用于结果()。如果为假,则后卫将生成列表monad的fail([]),将其应用于Kleisli箭头不会产生任何结果>>,因此将p其跳过。
州
臭名昭著的是,使用monad对状态计算进行编码。
甲状态处理器是一个函数
forall st t. st -> (t, st)
转换状态st并产生结果t。该状态 st可以是任何东西。没有任何内容,标志,计数,数组,句柄,机器,世界。
状态处理器的类型通常称为
type State st t = st -> (t, st)
状态处理程序monad是仁慈的* -> *函子State st。状态处理器monad的Kleisli箭头是函数
forall st a b. a -> (State st) b
在规范的Haskell中,定义了状态处理器monad的惰性版本
newtype State st t = State { stateProc :: st -> (t, st) }
instance Functor (State st) where
map :: (a -> b) -> ((State st) a -> (State st) b)
map f (State p) = State $ \ s0 -> let (x, s1) = p s0
in (f x, s1)
instance Monad (State st) where
return :: t -> (State st) t
return x = State $ \ s -> (x, s)
(=< (State st) b) -> (State st) a -> (State st) b
f =<< (State p) = State $ \ s0 -> let (x, s1) = p s0
in stateProc (f x) s1
通过提供初始状态来运行状态处理器:
run :: State st t -> st -> (t, st)
run = stateProc
eval :: State st t -> st -> t
eval = fst . run
exec :: State st t -> st -> st
exec = snd . run
状态访问由原语get和put,有状态单子抽象方法提供:
{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-}
class Monad m => Stateful m st | m -> st where
get :: m st
put :: st -> m ()
m -> st声明状态类型对monad 的功能依赖 ; 一个,例如,将确定的状态类型是唯一。stmState tt
instance Stateful (State st) st where
get :: State st st
get = State $ \ s -> (s, s)
put :: st -> State st ()
put s = State $ \ _ -> ((), s)
与void在C中类似使用的单位类型。
modify :: Stateful m st => (st -> st) -> m ()
modify f = do
s
put (f s)
gets :: Stateful m st => (st -> t) -> m t
gets f = do
s
return (f s)
gets 通常与记录字段访问器一起使用。
状态monad等效于变量线程
let s0 = 34
s1 = (+ 1) s0
n = (* 12) s1
s2 = (+ 7) s1
in (show n, s2)
其中s0 :: Int,是同样参照透明的,但无限地更加优雅和实用
(flip run) 34
(do
modify (+ 1)
n
modify (+ 7)
return (show n)
)
modify (+ 1)是类型的计算State Int (),除了其作用等同于return ()。
(flip run) 34
(modify (+ 1) >>
gets (* 12) >>= (\ n ->
modify (+ 7) >>
return (show n)
)
)
结合性的莫纳德定律可以写成 >>= forall m f g.
(m >>= f) >>= g = m >>= (\ x -> f x >>= g)
要么
do { do { do {
r1
r0
f r0 r1
}; g r1 }
g r1 }
} }
像面向表达式的编程(例如Rust)一样,块的最后一条语句表示其产量。绑定运算符有时称为“可编程分号”。
一元模拟来自结构化命令式编程的迭代控制结构原语
for :: Monad m => (a -> m b) -> [a] -> m ()
for f = foldr ((>>) . f) (return ())
while :: Monad m => m Bool -> m t -> m ()
while c m = do
b
if b then m >> while c m
else return ()
forever :: Monad m => m t
forever m = m >> forever m
输入输出
data World
I / O世界状态处理器monad是纯Haskell与现实世界,功能性说明性和命令性操作语义的协调。与实际严格执行的近似:
type IO t = World -> (t, World)
不纯净的原语促进了交互
getChar :: IO Char
putChar :: Char -> IO ()
readFile :: FilePath -> IO String
writeFile :: FilePath -> String -> IO ()
hSetBuffering :: Handle -> BufferMode -> IO ()
hTell :: Handle -> IO Integer
. . . . . .
使用IO原语的代码的杂物由类型系统永久地协议化。因为纯净真棒,所以发生在里面IO,留在里面IO。
unsafePerformIO :: IO t -> t
或者至少应该如此。
Haskell程序的类型签名
main :: IO ()
main = putStrLn "Hello, World!"
扩展到
World -> ((), World)
改变世界的功能。
结语
类别对象是Haskell类型,而态态是Haskell类型之间的函数,类别为“快速和宽松” Hask。
函子T是从类别C到类别的映射D; 对于一个对象中C的每个对象D
Tobj : Obj(C) -> Obj(D)
f :: * -> *
并且对于一个态中C的每个态D
Tmor : HomC(X, Y) -> HomD(Tobj(X), Tobj(Y))
map :: (a -> b) -> (f a -> f b)
其中X,Y在对象C。HomC(X, Y)是同态类的所有态射的X -> Y在C。仿函数必须保留态射特性和组合物,所述的“结构” C中D。
Tmor Tobj
T(id) = id : T(X) -> T(X) Identity
T(f) . T(g) = T(f . g) : T(X) -> T(Z) Composition
该Kleisli类一类C是由Kleisli三重给定
终结者的
T : C -> C
(f),身份同构eta(return)和扩展运算符*(=<
每个Kleisli态 Hask
f : X -> T(Y)
f :: a -> m b
由扩展运营商
(_)* : Hom(X, T(Y)) -> Hom(T(X), T(Y))
(=< m b) -> (m a -> m b)
在Hask的Kleisli类别中被赋予了态射
f* : T(X) -> T(Y)
(f =< m b
克莱斯里(Kleisli)类别的构成以.T扩展名给出
f .T g = f* . g : X -> T(Z)
f <=< g = (f =< m c
并满足类别公理
eta .T g = g : Y -> T(Z) Left identity
return <=< g = g :: b -> m c
f .T eta = f : Z -> T(U) Right identity
f <=< return = f :: c -> m d
(f .T g) .T h = f .T (g .T h) : X -> T(U) Associativity
(f <=< g) <=< h = f <=< (g <=< h) :: a -> m d
应用等价转换
eta .T g = g
eta* . g = g By definition of .T
eta* . g = id . g forall f. id . f = f
eta* = id forall f g h. f . h = g . h ==> f = g
(f .T g) .T h = f .T (g .T h)
(f* . g)* . h = f* . (g* . h) By definition of .T
(f* . g)* . h = f* . g* . h . is associative
(f* . g)* = f* . g* forall f g h. f . h = g . h ==> f = g
在扩展方面被规范地给出
eta* = id : T(X) -> T(X) Left identity
(return =< m t
f* . eta = f : Z -> T(U) Right identity
(f =< m d
(f* . g)* = f* . g* : T(X) -> T(Z) Associativity
(((f =< m c
也可以mu在编程中称为Monad,而不是根据Kleislian扩展定义而是自然转换join。monad被定义mu为Cendofunctor 的category 的三元组
T : C -> C
f :: * -> *
和两个自然转变
eta : Id -> T
return :: t -> f t
mu : T . T -> T
join :: f (f t) -> f t
满足等价