函子、适用函子与单位半群#
函子#
- 函子:
Functor
类型类的成员,即可映射对象,能对其内的每个元素进行变换; - 可以将函子视作在某个环境下输出特定值的类型(
Maybe
、[]
、函数等);
函子类型类#
class Functor f where
fmap :: (a -> b) -> f a -> f b
(<$) :: a -> f b -> f a
Functor
类型类:函子类型类,实现了fmap
函数和<$
运算符,函子是该类型类的成员,但该类型类的成员不一定都是合格的函子;1instance Functor Maybe where 2 fmap f (Just x) = Just (f x) 3 fmap f Nothing = Nothing 4 -- 因此 fmap :: (a -> b) -> Maybe a -> Maybe b 5 6instance Functor (Either a) where 7 fmap f (Right x) = Right (f x) 8 fmap f (Left x) = Left x 9 -- 因此 fmap :: (b -> c) -> Either a b -> Either a c 10 11import qualified Data.Map as M 12instance Functor (M.Map k) where 13 fmap f v = map f v 14 -- 因此 fmap :: (a -> b) -> M.Map k a -> M.Map k b
Functor
类型类只接受种类为* -> *
的类型构造器,不符合的构造器可先部分应用;1-- | 'Test' 种类为 @(* -> *) -> * -> * -> *@。 2data Test a b c = Test 3 { fir :: c -- ^ @c@ 为具体类型。 4 , sec :: a b -- ^ @b@ 为具体类型,@a b@ 相同。 5 -- 因此 @a@ 种类为 @* -> *@ 6 } 7 8-- | 部分应用 'Test' 后种类为 @* -> *@。 9instance Functor (Test a b) where 10 fmap f (Test { fir = x, sec = y }) = Test { fir = f x, sec = y }
函子方法#
-
GHC.Base.
fmap
:: Functor f => (a -> b) -> f a -> f b#
-
GHC.Base.
(<$>)
:: Functor f => (a -> b) -> f a -> f b# 接受一个函数和一个函子,对函子包含的每个值应用函数并返回新函子。
<$>
中缀运算符是fmap
函数的同义词,其命名是对$
运算符的化用。备注
可以将函子想象为容器,函数应用于容器中的值,再将结果放回容器。但要注意,“容器”只是一种比喻说法,更准确的术语为“计算上下文”。
exp1 = show (Just 3) -- "Just 3" exp2 = fmap show (Just 3) -- Just "3"
-
GHC.Base.
(<$)
:: Functor f => a -> f b -> f a# 接受一个值和一个函子,将函子内的每个值都替换为该值并返回新函子。
exp3 = 3 <$ Just 1 -- Just 3 exp4 = 'a' <$ [1 .. 4] -- "aaaa"
源码
1infixl 4 <$ 2 3(<$) :: a -> f b -> f a 4(<$) = fmap . const
函子成员#
Either a
exp1 = fmap (+ 100) (Right 2) -- Right 102 exp2 = 100 <$ Right 2 -- Right 100 exp3 = fmap (+ 100) (Left 2) -- Left 2 exp4 = 100 <$ Left 2 -- Left 2
源码
1instance Functor (Either a) where 2 fmap _ (Left x) = Left x 3 fmap f (Right y) = Right (f y)
Maybe
exp5 = fmap (+ 100) (Just 2) -- Just 102 exp6 = fmap (+ 100) Nothing -- Nothing exp7 = 100 <$ Just 2 -- Just 100 exp8 = 100 <$ Nothing -- Nothing
源码
1instance Functor Maybe where 2 fmap _ Nothing = Nothing 3 fmap f (Just a) = Just (f a)
[]
根据函子类型类的定义可得
fmap
函数应用于列表时的类型;fmap :: (a -> b) -> [] a -> [] b fmap :: (a -> b) -> [a] -> [b] map :: (a -> b) -> [a] -> [b]
由上一条可知,若
fmap
函数应用于列表,则fmap = map
;exp9 = fmap (+ 100) [1 .. 3] -- [101,102,103] exp10 = 100 <$ [1 .. 3] -- [100,100,100]
源码
1instance Functor [] where 2 fmap = map
IO
1import Data.Char 2 3-- | 将输入字符串转换为大写并倒序输出。 4main = do 5 -- getLine :: IO String 6 -- 因此 'f' 应用于 'String', 7 -- 即 'IO (f String)' 8 line <- fmap (reverse . map toUpper) getLine 9 putStrLn line 10 -- Alice in Wonderland 11 -- DNALREDNOW NI ECILA
源码
1instance Functor IO where 2 fmap f x = x >>= (pure . f)
(->) r
->
的实质也是类型类,接受两个类型参数,因此所有函数都是函子;可以将函数想象成包含了返回值的容器;
根据函子类型类的定义可得
fmap
函数应用于函数时的类型;fmap :: (a -> b) -> ((->) r a) -> ((->) r b) fmap :: (a -> b) -> (r -> a) -> r -> b (.) :: (b -> c) -> (a -> b) -> a -> c
由上一条可知,若
fmap
函数应用于函数,则fmap = (.)
;ghci> :t fmap (* 3) (+ 100) fmap (* 3) (+ 100) :: Num b => b -> b ghci> fmap (* 3) (+ 100) 1 303 ghci> (* 3) `fmap` (+ 100) $ 1 303 ghci> (* 3) . (+ 100) $ 1 303
源码
1instance Functor ((->) r) where 2 fmap = (.)
提升#
- 提升:在更通用的环境中,将一个函数转换为另一个函数;
fmap
函数的类型声明可以理解为fmap :: Functor f => (a -> b) -> (f a -> f b)
,即原本只能作用于普通值的一元函数,提升后可以作用于函子;
1ghci> :t fmap (* 100)
2fmap (* 100) :: (Functor f, Num b) => f b -> f b
3ghci> :t fmap (replicate 3)
4fmap (replicate 3) :: Functor f => f a -> f [a]
5ghci> mapReverse = fmap reverse
6ghci> :t mapReverse
7mapReverse :: Functor f => f [a] -> f [a]
8ghci> -- 提升后的函数可应用于函子
9ghci> plusOne = fmap (+ 1)
10ghci> plusOne $ Just 2
11Just 3
12ghci> plusOne [1 .. 3]
13[2,3,4]
14ghci> plusOne $ Right 2
15Right 3
函子规则#
备注
该两条规则和数学中函子的定义是一致的。
为确保代码的可扩展性和抽象性,
fmap
函数对函子应该只做映射操作,因此所有函子都应该遵守两条规则,满足这两条规则的类型可以作为函子;这两条规则 Haskell 并未强制实现,因此需要手动实现;
不满足这两条规则的
Functor
类型类成员可能导致不可预测的结果;1data CMaybe a = CNothing | CJust Int a deriving Show 2 3instance Functor CMaybe where 4 fmap f CNothing = CNothing 5 fmap f (CJust counter x) = CJust (counter + 1) (f x) 6 7exp3 = fmap id (CJust 0 "alice") -- CJust 1 "alice" 8exp4 = id (Cjust 0 "alice") -- CJust 0 "alice" 9 10exp5 = fmap (reverse . map succ) (CJust 0 "alice") 11 -- CJust 1 "fdjmb" 12exp6 = fmap reverse . fmap (map succ) $ CJust 0 "alice" 13 -- CJust 2 "fdjmb"
规则一:同等
fmap id == id
若fmap
函数将id
函数应用于函子,则返回结果应该与原函子完全相同。
instance Functor Maybe where
fmap f (Just x) = Just (f x)
-- fmap id (Just x) == Just (id x) == Just x == id Just x
fmap f Nothing = Nothing
-- fmap id Nothing == Nothing == id Nothing
规则二:组合
fmap (f . g) == fmap f . fmap g
对函子应用函数组合后的结果,应该与按顺序应用所有函数后的结果相同。
exp1 = fmap (reverse . map succ) (Just "Alice")
-- Just "fdjmB"
exp2 = fmap reverse . fmap (map succ) $ Just "Alice"
-- Just "fdjmB"
适用函子#
- 适用函子:
Applicative
类型类的成员,函子的增强版; Functor
类型类的方法不能处理两个普通函子间的运算,而适用函子类型类可以;
适用函子类型类#
class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
liftA2 :: (a -> b -> c) -> f a -> f b -> f c
(*>) :: f a -> f b -> f b
(<*) :: f a -> f b -> f a
Applicative
类型类:定义了上述方法的类型类,其成员同时也是Functor
类型类的成员;
适用函子方法#
-
GHC.Base.
pure
:: Applicative f => a -> f a# 接受一个值,将值打包进函子中,返回一个适用函子。
exp1 = pure 1 :: Maybe Int -- Just 1 exp2 = pure "alice" :: Either String String -- Right "alice"
-
GHC.Base.
(<*>)
:: Applicative f => f (a -> b) -> f a -> f b# 接受一个包含函数的函子和另一函子,将函数提取出来并应用于另一函子内的值,最终返回新函子。
exp3 = Just (+ 3) <*> Just 9 -- Just 12 exp4 = pure (+ 3) <*> Just 10 -- Just 13 exp5 = Just (++ "!") <*> Nothing -- Nothing
源码
1infixl 4 <*> 2 3(<*>) :: f (a -> b) -> f a -> f b 4(<*>) = liftA2 id
-
GHC.Base.
liftA2
:: Applicative f => (a -> b -> c) -> f a -> f b -> f c# 用于提升函数,即原本只能作用于普通值的二元函数,提升后可作用于两个函子。
import GHC.Base exp6 = liftA2 (+) (Just 1) (Just 2) -- Just 3 exp7 = liftA2 (*) (Just 3) Nothing -- Nothing
源码
1liftA2 :: (a -> b -> c) -> f a -> f b -> f c 2liftA2 f x = (<*>) (fmap f x)
-
GHC.Base.
(*>)
:: Applicative f => f a -> f b -> f b# 丢弃第一个参数,选择第二个参数。
exp8 = Just 1 *> Just 2 -- Just 2
源码
1infixl 4 *> 2 3(*>) :: f a -> f b -> f b 4a1 *> a2 = (id <$ a1) <*> a2
-
GHC.Base.
(<*)
:: Applicative f => f a -> f b -> f a# 丢弃第二个参数,选择第一个参数。
exp9 = Just 1 <* Just 2 -- Just 1
源码
1infixl 4 <* 2 3(<*) :: f a -> f b -> f a 4(<*) = liftA2 const
适用函子成员#
Either e
import GHC.Base exp1 = pure 1 :: Either e Int -- Right 1 exp2 = Right (+ 100) <*> Right 2 -- Right 102 exp3 = liftA2 (+) (Right 3) (Right 4) -- Right 7
源码
1instance Applicative (Either e) where 2 pure = Right 3 Left e <*> _ = Left e 4 Right f <*> r = fmap f r
[]
由于列表的结果是非确定的,因此
<*>
会遍历提取列表所有元素并应用于右侧列表所有元素上,返回的列表包含所有排列组合的结果;<*>
可用来替代列表推导式;exp4 = [ x * y | x <- [2, 5, 10], y <- [8, 10, 11] ] -- [16,20,22,40,50,55,80,100,110] exp5 = (*) <$> [2, 5, 10] <*> [8, 10, 11] -- [16,20,22,40,50,55,80,100,110]
对于列表,
pure f <*> xs
等价于fmap f xs
;
exp6 = [(+ 1),(+ 2)] <*> pure 4 -- [5,6] exp7 = liftA2 (*) [1 .. 3] [2 .. 4] -- [1*2,1*3,1*4,2*2,2*3,2*4,3*2,3*3,3*4] = [2,3,4,4,6,8,6,9,12]
源码
1instance Applicative [] where 2 {-# INLINE pure #-} 3 pure x = [x] 4 {-# INLINE (<*>) #-} 5 fs <*> xs = [ f x | f <- fs, x <- xs ] 6 {-# INLINE liftA2 #-} 7 liftA2 f xs ys = [ f x y | x <- xs, y <- ys ] 8 {-# INLINE (*>) #-} 9 xs *> ys = [ y | _ <- xs, y <- ys ]
Maybe
exp8 = Just (+ 3) <*> Nothing -- Nothing
源码
1instance Applicative Maybe where 2 pure = Just 3 4 Just f <*> m = fmap f m 5 Nothing <*> _m = Nothing 6 7 liftA2 f (Just x) (Just y) = Just (f x y) 8 liftA2 _ _ _ = Nothing 9 10 Just _m1 *> m2 = m2 11 Nothing *> _m2 = Nothing
IO
exp9 <- pure (++ "!") <*> getLine -- Alice -- "Alice!" exp10 <- (++) <$> getLine <*> getLine -- Hello -- World -- "HelloWorld"
(->) r
- 对于函数,
pure
等价于const
; - 从左侧函数提取出返回值后应用于右侧函数的返回值,返回结果;
exp11 = pure 3 "blah" -- 3 exp12 = (+) <*> (* 100) $ 2 -- (+2) (2*100) = 202
源码
1instance Applicative ((->) r) where 2 pure = const 3 (<*>) f g x = f x (g x) 4 liftA2 q f g x = q (f x) (g x)
- 对于函数,
Control.Applicative.ZipList
对普通列表使用
<*>
时,结果可看作两个列表元素的排列组合,而对ZipList
类型使用<*>
时,可对列表对应位置的元素进行操作(长度为最短列表的长度);源码
1newtype ZipList a = ZipList { getZipList :: [a] } 2 deriving ( Show -- ^ @since 4.7.0.0 3 , Eq -- ^ @since 4.7.0.0 4 , Ord -- ^ @since 4.7.0.0 5 , Read -- ^ @since 4.7.0.0 6 , Functor -- ^ @since 2.01 7 , Foldable -- ^ @since 4.9.0.0 8 , Generic -- ^ @since 4.7.0.0 9 , Generic1 -- ^ @since 4.7.0.0 10 )
对
ZipList
类型应用pure
时返回无限列表,满足pure f <*> xs == fmap f xs
;
exp13 = getZipList $ (+) <$> ZipList [1, 2, 3] <*> ZipList [100, 100, 100] -- -> getZipList $ ZipList[(+1),(+2),(+3)] <*> ZipList [100,100,100] -- -> getZipList $ ZipList [((+1) 100),((+2) 100),((+3) 100)] -- -> [101,102,103] exp14 = getZipList $ pure (+ 3) <*> ZipList [1, 2, 3] -- -> getZipList $ ZipList [(+3),(+3),(+3),...] <*> ZipList [1,2,3] -- -> getZipList $ ZipList [((+3) 1),((+3) 2),((+3) 3)] -- -> [4,5,6]
源码
1instance Applicative ZipList where 2 pure x = ZipList (repeat x) 3 liftA2 f (ZipList xs) (ZipList ys) = ZipList (zipWith f xs ys)
适用函子规则#
- 和函子相同,适用函子同样需要遵循一定的规则;
规则一:同等
pure id <*> v = v
exp1 = pure id <*> Just 2 -- Just 2
规则二:组合
pure (.) <*> u <*> v <*> w = u <*> (v <*> w)
exp2 = pure (.) <*> Just (+ 3) <*> Just (* 4) <*> Just 5 -- Just 23
exp3 = Just (+ 3) <*> (Just (* 4) <*> Just 5) -- Just 23
规则三:同态
pure f <*> pure x = pure (f x)
exp4 = pure (+ 2) <*> pure 3 -- 5
exp5 = pure ((+ 2) 3) -- 5
规则四:交换
u <*> pure y = pure ($ y) <*> u
exp6 = Just (+ 2) <*> pure 3 -- Just 5
exp7 = pure ($ 3) <*> Just (+ 2) -- Just 5
单位半群#
定义#
半群:对于非空集合 \(S\) 和二元运算操作 \(\circ : S \times S \to S\),若 \(\forall x, y, z \in S\),满足 \((x \circ y) \circ z = x \circ (y \circ z)\),则称二元元组 \((S, \circ)\) 为半群,简称为半群 \(S\);
- 这种性质称为结合性;
备注
对于自然数集 \(\mathbb{N}\),有乘法运算 \(\times\),且 \(\forall x, y ,z \in \mathbb{N}\),满足 \((x \times y) \times z = x \times (y \times z)\),因此 \((\mathbb{N}, \times)\) 为半群。
单位半群:对于半群 \(S\),若 \(\exists e\),有 \(\forall a \in S\),满足 \(a \circ e = e \circ a = a\),则称三元元组 \((S, \circ, e)\) 为单位半群,也称为幺半群;
- \(e\) 称为单位元;
备注
对于半群 \((\mathbb{N}, \times)\),存在单位元 \(1\),对于 \(\forall a \in \mathbb{N}\),满足 \(a \times 1 = 1 \times a = a\),因此 \((\mathbb{N}, \times, 1)\) 为单位半群。
Haskell 中
Semigroup
和Monoid
类型类的定义与数学定义基本一致;
半群类型类#
class Semigroup a where
(<>) :: a -> a -> a
sconcat :: NonEmpty a -> a
stimes :: Integral b => b -> a -> a
Semigroup
类型类:接受一个具体类型;
半群方法#
-
GHC.Base.
(<>)
:: Semigroup a => a -> a -> a# 接受两个半群,以特定方式结合后返回第三个半群。
源码
1infixr 6 <>
-
GHC.Base.
stimes
:: (Semigroup a, Integral b) => b -> a -> a# 接受一个整型值和一个半群,将半群中的值重复整型值次,返回结果半群。
res = stimes 4 [1, 2] -- [1,2,1,2,1,2,1,2]
单位半群类型类#
class Semigroup a => Monoid a where
mempty :: a
mappend :: a -> a -> a
mconcat :: [a] -> a
Monoid
类型类:接受一个具体类型,同时该类型也是Semigroup
类型类的成员;
单位半群方法#
-
GHC.Base.
mempty
:: Monoid a => a# 本质为多态常量,代表该单位半群的单位元。
exp1 = mempty :: [a] -- [] exp2 = mempty :: String -- "" exp3 = mempty :: ([a], [b]) -- ([],[])
-
GHC.Base.
mappend
:: Monoid a => a -> a -> a# 该函数和
<>
运算符是同义词,但接受两个单位半群。exp4 = mappend [] [1] -- [1] exp5 = mappend "Hello" "World" -- "HelloWorld"
备注
mappend
函数的命名稍微有些费解。虽然函数名带“append”,但并不代表该函数将两个值追加在一起。实际上该函数只是一个二元函数,接受两个值并返回第三个值。注意
该函数是
<>
运算符的同义词,因此稍显冗余。Haskell 会在未来版本中移除该函数。源码
1mappend :: a -> a -> a 2mappend = (<>) 3{-# INLINE mappend #-}
单位半群成员#
[a]
exp1 = ("one" <> "two") <> "three" -- "onetwothree" exp2 = "one" <> ("two" <> "three") -- "onetwothree" exp3 = "one" <> mempty -- "one" exp4 = mconcat [[1, 2], [3, 6], [9]] -- [1,2,3,6,9] exp5 = mempty :: [a] -- []
源码
1import Data.Semigroup.Internal (stimesList) 2 3instance Semigroup [a] where 4 (<>) = (++) 5 {-# INLINE (<>) #-} 6 7 stimes = stimesList 8 9instance Monoid [a] where 10 {-# INLINE mempty #-} 11 mempty = [] 12 {-# INLINE mconcat #-} 13 mconcat xss = [ x | xs <- xss, x <- xs ]
Product a
Product
定义于Data.Semigroup
模块,newtype
数字类型,表示数字的乘积;源码
1newtype Product a = Product { getProduct :: a } 2 deriving ( Eq -- ^ @since 2.01 3 , Ord -- ^ @since 2.01 4 , Read -- ^ @since 2.01 5 , Show -- ^ @since 2.01 6 , Bounded -- ^ @since 2.01 7 , Generic -- ^ @since 4.7.0.0 8 , Generic1 -- ^ @since 4.7.0.0 9 , Num -- ^ @since 4.7.0.0 10 )
Product
类型的二元运算为乘法,单位元为Product 1
;
exp6 = getProduct $ Product 2 <> Product 3 -- 6 exp7 = getProduct . mconcat . map Product $ [1..5] -- 120 exp8 = getProduct mempty -- 1
源码
1instance Num a => Semigroup (Product a) where 2 (<>) = coerce ((*) :: a -> a -> a) 3 stimes n (Product a) = Product (a ^ n) 4 5instance Num a => Monoid (Product a) where 6 mempty = Product 1
Sum a
Sum
定义于Data.Semigroup
模块,newtype
数字类型,表示数字的和;源码
1newtype Sum a = Sum { getSum :: a } 2 deriving ( Eq -- ^ @since 2.01 3 , Ord -- ^ @since 2.01 4 , Read -- ^ @since 2.01 5 , Show -- ^ @since 2.01 6 , Bounded -- ^ @since 2.01 7 , Generic -- ^ @since 4.7.0.0 8 , Generic1 -- ^ @since 4.7.0.0 9 , Num -- ^ @since 4.7.0.0 10 )
Sum
类型的二元运算为加法,单位元为Sum 0
;
exp9 = getSum $ Sum 2 <> Sum 9 -- 11 exp10 = getSum . mconcat . map Sum $ [1..5] -- 15
源码
1instance Num a => Semigroup (Sum a) where 2 (<>) = coerce ((+) :: a -> a -> a) 3 stimes n (Sum a) = Sum (fromIntegral n * a) 4 5instance Num a => Monoid (Sum a) where 6 mempty = Sum 0
Any
定义于
Data.Semigroup
模块,newtype
布尔类型,表示存在一个布尔值为真;源码
1newtype Any = Any { getAny :: Bool } 2 deriving ( Eq -- ^ @since 2.01 3 , Ord -- ^ @since 2.01 4 , Read -- ^ @since 2.01 5 , Show -- ^ @since 2.01 6 , Bounded -- ^ @since 2.01 7 , Generic -- ^ @since 4.7.0.0 8 )
Any
类型的二元运算为逻辑或,单位元为False
;
exp11 = getAny $ Any True <> Any False -- True exp12 = getAny . mconcat . map Any $ [False, True, False] -- True
源码
1instance Semigroup Any where 2 (<>) = coerce (||) 3 stimes = stimesIdempotentMonoid 4 5instance Monoid Any where 6 mempty = Any False
All
定义于
Data.Semigroup
模块,newtype
布尔类型,表示所有布尔值均为真;源码
1newtype All = All { getAll :: Bool } 2 deriving ( Eq -- ^ @since 2.01 3 , Ord -- ^ @since 2.01 4 , Read -- ^ @since 2.01 5 , Show -- ^ @since 2.01 6 , Bounded -- ^ @since 2.01 7 , Generic -- ^ @since 4.7.0.0 8 )
All
类型的二元运算为逻辑和,单位元为All True
;
exp13 = getAll $ All True <> All False -- False exp14 = getAll . mconcat . map All $ [True, True, True] -- True
源码
1instance Semigroup All where 2 (<>) = coerce (&&) 3 stimes = stimesIdempotentMonoid 4 5instance Monoid All where 6 mempty = All True
Ordering
Ordering
类型也是Monoid
类型类的成员,单位元为EQ
;Ordering
类型的二元运算始终保留左侧值,除非左侧值为EQ
;exp15 = (LT <> GT) <> GT -- LT exp16 = LT <> (GT <> GT) -- LT exp17 = EQ <> GT -- GT exp18 = GT <> EQ -- GT
Ordering
类型作为Monoid
类型类的成员,让对象间的比较方式更加丰富,且允许按照重要程度对比较方式进行排序;
小技巧
可以将
Ordering
的单位半群想象为英语词典词条排序的过程。若两个词条的首字母不同(LT
或GT
),则比较结束,直接返回结果;若首字母相同(EQ
),则比较下一字母。1-- | 比较两个字符串长度。 2-- 若长度相同,则比较元音字母数量。 3-- 若元音数量相同,则比较字母顺序。 4-- 5-- ==== __例子:__ 6-- >>> lengthCompare "o" "on" 7-- LT 8-- 9-- >>> lengthCompare "zen" "ana" 10-- LT 11-- 12-- >>> lengthCompare "ox" "on" 13-- GT 14lengthCompare :: String -> String -> Ordering 15lengthCompare x y = 16 (length x `compare` length y) 17 <> (vowels x `compare` vowels y) 18 <> (x `compare` y) 19 where vowels = length . filter (`elem` "aeiou")
源码
1instance Semigroup Ordering where 2 LT <> _ = LT 3 EQ <> y = y 4 GT <> _ = GT 5 6 stimes = stimesIdempotentMonoid 7 8instance Monoid Ordering where 9 mempty = EQ
Maybe a
Maybe
类型的单位元为Nothing
;始终对
Maybe
类型的值应用mappend
函数,除非值为Nothing
,此时保留另一个值;Maybe
类型的类型参数也必须为Semigroup
类型类的成员;exp19 = Just "alice" <> Nothing -- Just "alice" exp20 = Nothing <> Just LT -- Just LT exp21 = Just (Sum 3) <> Just (Sum 4) -- Just (Sum {getSum = 7})
源码
1instance Semigroup a => Semigroup (Maybe a) where 2 Nothing <> b = b 3 a <> Nothing = a 4 Just a <> Just b = Just (a <> b) 5 6 stimes = stimesMaybe 7 8instance Semigroup a => Monoid (Maybe a) where 9 mempty = Nothing
First a
First
定义于Data.Monoid
模块,newtype
Maybe
类型,表示最左侧的非Nothing
值;源码
1newtype First a = First { getFirst :: Maybe a } 2 deriving ( Eq -- ^ @since 2.01 3 , Ord -- ^ @since 2.01 4 , Read -- ^ @since 2.01 5 , Show -- ^ @since 2.01 6 , Generic -- ^ @since 4.7.0.0 7 , Generic1 -- ^ @since 4.7.0.0 8 , Functor -- ^ @since 4.8.0.0 9 , Applicative -- ^ @since 4.8.0.0 10 , Monad -- ^ @since 4.8.0.0 11 )
First
类型保留最左侧的值,若左值为Nothing
,则保留右值,单位元为First Nothing
;
exp22 = getFirst $ First (Just 'a') <> First (Just 'b') -- Just 'a' exp23 = getFirst . mconcat . map First $ [Nothing, Just 1] -- Just 1
源码
1instance Semigroup (First a) where 2 First Nothing <> b = b 3 a <> _ = a 4 stimes = stimesIdempotentMonoid 5 6instance Monoid (First a) where 7 mempty = First Nothing
Last a
Last
定义于Data.Monoid
模块,newtype
Maybe
类型,表示最右侧的非Nothing
值;源码
1newtype Last a = Last { getLast :: Maybe a } 2 deriving ( Eq -- ^ @since 2.01 3 , Ord -- ^ @since 2.01 4 , Read -- ^ @since 2.01 5 , Show -- ^ @since 2.01 6 , Generic -- ^ @since 4.7.0.0 7 , Generic1 -- ^ @since 4.7.0.0 8 , Functor -- ^ @since 4.8.0.0 9 , Applicative -- ^ @since 4.8.0.0 10 , Monad -- ^ @since 4.8.0.0 11 )
Last
类型保留最右侧的值,若右值为Nothing
,则保留左值,单位元为Last Nothing
;
exp24 = getLast $ Last (Just 1) <> Last (Just 2) -- Just 2 exp25 = getLast . mconcat . map Last $ [Just 1, Just 2, Nothing] -- Just 2
源码
1instance Semigroup (Last a) where 2 a <> Last Nothing = a 3 _ <> b = b 4 stimes = stimesIdempotentMonoid 5 6instance Monoid (Last a) where 7 mempty = Last Nothing