instance Monad [ ] where[] >>= k = [](x:xs) >>= k = k x ++ (xs >>= k)return x = [x]
instance Monad Maybe whereJust x >>= k = k xNothing >>= k = Nothingreturn x = Just x
trait M[+A] {def flatMap[B](f: A => M[B]): M[B] // AKA bind
// Pseudo Meta Codedef isValidMonad: Boolean = {// for every parameter the following holdsdef isAssociativeOn[X, Y, Z](x: M[X], f: X => M[Y], g: Y => M[Z]): Boolean =x.flatMap(f).flatMap(g) == x.flatMap(f(_).flatMap(g))
// for every parameter X and x, there exists an id// such that the following holdsdef isAnIdentity[X](x: M[X], id: X => M[X]): Boolean =x.flatMap(id) == x}}
例如。
// These could be any functionsval f: Int => Option[String] = number => if (number == 7) Some("hello") else Noneval g: String => Option[Double] = string => Some(3.14)
// Observe these are identical. Since Option is a Monad// they will always be identical no matter what the functions arescala> Some(7).flatMap(f).flatMap(g)res211: Option[Double] = Some(3.14)
scala> Some(7).flatMap(f(_).flatMap(g))res212: Option[Double] = Some(3.14)
// As Option is a Monad, there exists an identity:val id: Int => Option[Int] = x => Some(x)
// Observe these are identicalscala> Some(7).flatMap(id)res213: Option[Int] = Some(7)
scala> Some(7)res214: Some[Int] = Some(7)
//JavaScript is 'Practical'var getAllThree =bind(getFirst, function(first){return bind(getSecond,function(second){return bind(getThird, function(third){var fancyResult = // And now make do fancy// with first, second,// and thirdreturn RETURN(fancyResult);});});});
但是monad启用了这样的代码。 monad实际上是以下类型的集合: {bind,RETURN,maybe others I don't know...}. 基本上不必要,实际上不切实际。
现在我可以使用它:
var fancyResultReferenceOutsideOfMonad =getAllThree(someKindOfInputAcceptableToOurGetFunctionsButProbablyAString);
//Ignore this please, throwing away types, yay JavaScript:// RETURN = K// bind = \getterFn,cb ->// \in -> let(result,newState) = getterFn(in) in cb(result)(newState)
或者打破它:
var getFirstTwo =bind(getFirst, function(first){return bind(getSecond,function(second){var fancyResult2 = // And now make do fancy// with first and secondreturn RETURN(fancyResult2);});}), getAllThree =bind(getFirstTwo, function(fancyResult2){return bind(getThird, function(third){var fancyResult3 = // And now make do fancy// with fancyResult2,// and thirdreturn RETURN(fancyResult3);});});
或者忽略某些结果:
var getFirstTwo =bind(getFirst, function(first){return bind(getSecond,function(second){var fancyResult2 = // And now make do fancy// with first and secondreturn RETURN(fancyResult2);});}), getAllThree =bind(getFirstTwo, function(____dontCare____NotGonnaUse____){return bind(getThird, function(three){var fancyResult3 = // And now make do fancy// with `three` only!return RETURN(fancyResult3);});});
或者简化一个琐碎的案例:
var getFirstTwo =bind(getFirst, function(first){return bind(getSecond,function(second){var fancyResult2 = // And now make do fancy// with first and secondreturn RETURN(fancyResult2);});}), getAllThree =bind(getFirstTwo, function(_){return bind(getThird, function(three){return RETURN(three);});});
var getFirstTwo =bind(getFirst, function(first){return bind(getSecond,function(second){var fancyResult2 = // And now make do fancy// with first and secondreturn RETURN(fancyResult2);});}), getAllThree =bind(getFirstTwo, function(_){return getThird;});
或者把它们粘在一起:
var getAllThree =bind(getFirst, function(first_dontCareNow){return bind(getSecond,function(second_dontCareNow){return getThird;});});
(Yah what is it?)(... hm? Oh, |forget about it. |Hey a, yr up.) |\ |(Evaluation \ |time already? \ |Hows my hair?) | || / || (It's || fine.) /| / /{| a |m} >>= f
但对于Nothing的情况
(Yah what is it?)(... There |is no a. ) || (No a?)(No a.) || (Ok, I'll deal| with this.)\ |\ (Hey f, get lost.)\ | ( Where's my a?\ | I evaluate a)\ (Not any more |\ you don't. || We're returning| Nothing.) /| | /| | /| | /{| a |m} >>= f (I got a b.)| (This is \| such a \| sham.) o o \| o||--later-> {| b |m}
(Ok, here's your a. Well, itsa bunch of them, actually.)|| (Thanks, no problem. Ok| f, here you go, an a.)| || | (Thank's. See| | you later.)| (Whoa. Hold up f, || I got another || a for you.) || | (What? No, sorry.| | Can't do it. I| | have my hands full| | with all these "b"| | I just made.)| (I'll hold those, || you take this, and /| come back for more /| when you're done /| and we'll do it /| again.) /\ | ( Uhhh. All right.)\ | /\ \ /{| a |m} >>= f
{-# LANGUAGE InstanceSigs #-}
newtype Id t = Id t
instance Monad Id wherereturn :: t -> Id treturn = Id
(=<<) :: (a -> Id b) -> Id a -> Id bf =<< (Id x) = f x
序幕
函数的应用程序运算符$
forall a b. a -> b
是规范定义的
($) :: (a -> b) -> a -> bf $ 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 identityid . g = g :: b -> c Left identity(f . g) . h = f . (g . h) :: a -> d Associativity
{-# LANGUAGE KindSignatures #-}
class Functor (f :: * -> *) wheremap :: (a -> b) -> (f a -> f b)
除了遵循静态强制类型协议外,仿函数类型类的实例必须遵守代数仿函数定律forall f g.
map id = id :: f t -> f t Identitymap f . map g = map (f . g) :: f a -> f c Composition / short cut fusion
函数计算的类型
forall f t. Functor f => f t
计算c r包含在背景c中的结果r中。
一元一元函数或Kleisli箭头具有以下类型
forall m a b. Functor m => a -> m b
Kleisi箭头是接受一个参数a并返回一元计算m b的函数。
单子是根据Kleisli三重forall m. Functor m =>规范定义的
(m, return, (=<<))
实现为类型类
class Functor m => Monad m wherereturn :: t -> m t(=<<) :: (a -> m b) -> m a -> m b
infixr 1 =<<
Kleisli身份return是一个Kleisli箭头,它将值t提升到一元上下文m。扩展或Kleisli应用=<<将Kleisli箭头a -> m b应用于计算m a的结果。
Kleisli构图<=<在扩展方面定义为
(<=<) :: Monad m => (b -> m c) -> (a -> m b) -> (a -> m c)f <=< g = \ x -> f =<< g x
infixr 1 <=<
<=<组成两个Kleisli箭头,将左箭头应用于右箭头应用的结果。
monad类型类的实例必须遵守单子定律,这是Kleisli组合中最优雅的陈述:forall f g h.
f <=< return = f :: c -> m d Right identityreturn <=< g = g :: b -> m c Left identity(f <=< g) <=< h = f <=< (g <=< h) :: a -> m d Associativity
<=<是关联的,return是它的左右标识。
身份
的身份类型
type Id t = t
是类型上的单位函数
Id :: * -> *
解释为仿函数,
return :: t -> Id t= id :: t -> t
(=<<) :: (a -> Id b) -> Id a -> Id b= ($) :: (a -> b) -> a -> b
(<=<) :: (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 wheremap :: (a -> b) -> Id a -> Id bmap f (Id x) = Id (f x)
instance Monad Id wherereturn :: t -> Id treturn = Id
(=<<) :: (a -> Id b) -> Id a -> Id bf =<< (Id x) = f x
选项
选项类型
data Maybe t = Nothing | Just t
编码不一定产生结果t的计算Maybe t,可能“失败”的计算。定义了选项monad
instance Functor Maybe wheremap :: (a -> b) -> (Maybe a -> Maybe b)map f (Just x) = Just (f x)map _ Nothing = Nothing
instance Monad Maybe wherereturn :: t -> Maybe treturn = Just
(=<<) :: (a -> Maybe b) -> Maybe a -> Maybe bf =<< (Just x) = f x_ =<< Nothing = Nothing
只有当Maybe a产生结果时,a -> Maybe b才应用于结果。
newtype Nat = Nat Int
自然数可以被编码为大于或等于零的整数。
toNat :: Int -> Maybe NattoNat i | i >= 0 = Just (Nat i)| otherwise = Nothing
自然数在减法下不闭合。
(-?) :: Nat -> Nat -> Maybe Nat(Nat n) -? (Nat m) = toNat (n - m)
infixl 6 -?
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类型类时,Haskell标准使用它的翻盖,绑定运算符>>=,而不是扩展名=<<。
class Applicative m => Monad m where(>>=) :: forall a b. m a -> (a -> m b) -> m b
(>>) :: forall a b. m a -> m b -> m bm >> k = m >>= \ _ -> k{-# INLINE (>>) #-}
return :: a -> m areturn = pure
为了简单起见,这个解释使用类型类层次结构
class Functor fclass Functor m => Monad m
在Haskell中,当前的标准层次结构是
class Functor fclass Functor p => Applicative pclass Applicative m => Monad m
因为不仅每个monad都是仿函数,而且每个应用程序都是仿函数,每个monad也是应用程序。
使用list monad,命令式伪代码
for a in (1, ..., 10)for b in (1, ..., 10)p <- a * bif even(p)yield p
大致翻译为确实阻止,
do a <- [1 .. 10]b <- [1 .. 10]let p = a * bguard (even p)return p
等价的单子理解,
[ p | a <- [1 .. 10], b <- [1 .. 10], let p = a * b, even p ]
和表达
[1 .. 10] >>= (\ a ->[1 .. 10] >>= (\ b ->let p = a * b inguard (even p) >> -- [ () | even p ] >>return p))
Do表示法和monad理解是嵌套绑定表达式的语法糖。绑定运算符用于一元结果的本地名称绑定。
let x = v in e = (\ x -> e) $ v = v & (\ x -> e)do { r <- m; c } = (\ 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 wherefail :: m t(<|>) :: m t -> m t -> m t
infixl 3 <|>
instance Additive Maybe wherefail = Nothing
Nothing <|> m = mm <|> _ = m
instance Additive [] wherefail = [](<|>) = (++)
其中fail和<|>形成一个二次单体
k <|> fail = kfail <|> l = l(k <|> l) <|> m = k <|> (l <|> m)
newtype State st t = State { stateProc :: st -> (t, st) }
instance Functor (State st) wheremap :: (a -> b) -> ((State st) a -> (State st) b)map f (State p) = State $ \ s0 -> let (x, s1) = p s0in (f x, s1)
instance Monad (State st) wherereturn :: t -> (State st) treturn x = State $ \ s -> (x, s)
(=<<) :: (a -> (State st) b) -> (State st) a -> (State st) bf =<< (State p) = State $ \ s0 -> let (x, s1) = p s0in stateProc (f x) s1
状态处理器通过提供初始状态来运行:
run :: State st t -> st -> (t, st)run = stateProc
eval :: State st t -> st -> teval = fst . run
exec :: State st t -> st -> stexec = snd . run
状态访问由原语get和put提供,抽象方法超过有状态 monad:
{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-}
class Monad m => Stateful m st | m -> st whereget :: m stput :: st -> m ()
m -> st在monadm上声明了状态类型st的功能依赖;例如,State t将唯一地确定状态类型为t。
instance Stateful (State st) st whereget :: State st stget = State $ \ s -> (s, s)
put :: st -> State st ()put s = State $ \ _ -> ((), s)
单位类型类似于C中的void。
modify :: Stateful m st => (st -> st) -> m ()modify f = dos <- getput (f s)
gets :: Stateful m st => (st -> t) -> m tgets f = dos <- getreturn (f s)
gets通常与记录字段访问器一起使用。
变量线程的状态monad等效
let s0 = 34s1 = (+ 1) s0n = (* 12) s1s2 = (+ 7) s1in (show n, s2)
for :: Monad m => (a -> m b) -> [a] -> m ()for f = foldr ((>>) . f) (return ())
while :: Monad m => m Bool -> m t -> m ()while c m = dob <- cif b then m >> while c melse return ()
forever :: Monad m => m tforever m = m >> forever m
(_)* : Hom(X, T(Y)) -> Hom(T(X), T(Y))(=<<) :: (a -> m b) -> (m a -> m b)
在Hask的Kleisli范畴中给定一个态射
f* : T(X) -> T(Y)(f =<<) :: m a -> m b
Kleisli类别.T中的组成以扩展形式给出
f .T g = f* . g : X -> T(Z)f <=< g = (f =<<) . g :: a -> m c
并满足范畴公理
eta .T g = g : Y -> T(Z) Left identityreturn <=< g = g :: b -> m c
f .T eta = f : Z -> T(U) Right identityf <=< 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 = geta* . g = g By definition of .Teta* . g = id . g forall f. id . f = feta* = 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 =<<) = id :: m t -> m t
f* . eta = f : Z -> T(U) Right identity(f =<<) . return = f :: c -> m d
(f* . g)* = f* . g* : T(X) -> T(Z) Associativity(((f =<<) . g) =<<) = (f =<<) . (g =<<) :: m a -> m c
eta : Id -> Treturn :: t -> f t
mu : T . T -> Tjoin :: f (f t) -> f t
满足等价
mu . T(mu) = mu . mu : T . T . T -> T . T Associativityjoin . map join = join . join :: f (f (f t)) -> f t
mu . T(eta) = mu . eta = id : T -> T Identityjoin . map return = join . return = id :: f t -> f t
然后定义monad类型类
class Functor m => Monad m wherereturn :: t -> m tjoin :: m (m t) -> m t
选项monad的规范mu实现:
instance Monad Maybe wherereturn = Just
join (Just m) = mjoin Nothing = Nothing
join Nothing = Nothingjoin (Just Nothing) = Nothingjoin (Just x) = xa >>= f = join (fmap f a)
或者更明确地说:
Nothing >>= _ = Nothing(Just x) >>= f = f x
State Monad用于修改某些共享状态的函数-s -> (a, s),因此>>=的参数是:: a -> s -> (a, s)。 这个名字是一种用词不当,因为State实际上是用于修改状态的函数,而不是用于状态-状态本身真的没有有趣的属性,它只是被改变了。
例如:
pop :: [a] -> (a , [a])pop (h:t) = (h, t)sPop = state pop -- The module for State exports no State constructor,-- only a state function
push :: a -> [a] -> ((), [a])push x l = ((), x : l)sPush = state push
swap = do a <- sPopb <- sPopsPush asPush b
get2 = do a <- sPopb <- sPopreturn (a, b)
getswapped = do swapget2
join :: [[a]] -> [a] -- for lists, or nondeterministic valuesjoin :: Maybe (Maybe a) -> Maybe a -- for Maybe, or optional valuesjoin :: IO (IO a) -> IO a -- for I/O-produced values
(ma >>= k) == join (fmap k ma){-ma :: m a -- `m`-computation which produces `a`-type valuesk :: a -> m b -- create new `m`-computation from an `a`-type valuefmap k ma :: m ( m b ) -- `m`-computation of `m`-computation of `b`-type values(m >>= k) :: m b -- `m`-computation which produces `b`-type values-}
相反,join可以通过bind定义,join mma == join (fmap id mma) == mma >>= id其中id ma = ma——对于给定类型m更方便的那个。
对于monad,#0符号和等效的绑定使用代码,
do { x <- mx ; y <- my ; return (f x y) } -- x :: a , mx :: m a-- y :: b , my :: m bmx >>= (\x -> -- nestedmy >>= (\y -> -- lambdareturn (f x y) )) -- functions
(1)与liftA2 :: Applicative m => (a -> b -> c) -> m a -> m b -> m c
(2)与pure :: Applicative m => a -> m a
(3)与fmap :: Functor m => (a -> b) -> m a -> m b
还有等价的Monad方法,
liftM2 :: Monad m => (a -> b -> c) -> m a -> m b -> m creturn :: Monad m => a -> m aliftM :: Monad m => (a -> b) -> m a -> m b
给定一个monad,其他定义可以是
pure a = return afmap f ma = do { a <- ma ; return (f a) }liftA2 f ma mb = do { a <- ma ; b <- mb ; return (f a b) }(ma >>= k) = do { a <- ma ; b <- k a ; return b }