内容简介:这一系列是我学习这个系列主要分为五个部分:TypeClass,我们在第二篇中就讲过,与命令式编程不同,Haskell中的class不是类,而是更像 "接口"这个概念,或者说,"类型类"。比如我们有个接口是能比较是否相等:
这一系列是我学习 Learn You a Haskell For Great Good
之后,总结,编写的学习笔记。
这个系列主要分为五个部分:
- 从递归说起
- 从命令式语言进行抽象
- Haskell初步:语法
- Haskell进阶:Monoid, Applicative, Monad
- 实战:Haskell和JSON
回忆TypeClass
TypeClass,我们在第二篇中就讲过,与命令式编程不同,Haskell中的class不是类,而是更像 "接口"这个概念,或者说,"类型类"。比如我们有个接口是能比较是否相等:
class Equalable a where equal :: a -> a -> Bool uneuqal :: a -> a -> Bool equal x y = not $ uneuqal x y uneuqal x y = not $ equal x y
首先我们可以看到 Equalable 针对一个类a,其中类型声明, equal :: a -> a -> Bool
表示 equal
这个函数接受两个a类型的参数,然后返回一个布尔类型的值。并且我们提供了默认实现, equal
就是 uneuqal
的反, uneuqal
就是 equal
的反。我们来看看Int是怎么实现这个接口的:
instance Equalable Int where equal x y = x == y
运行一下:
Prelude> :load Demo.hs [1 of 1] Compiling Main ( Demo.hs, interpreted ) Ok, 1 module loaded. *Main> let a = 1 :: Int *Main> let b = 1 :: Int *Main> let c = 2 :: Int *Main> a `equal` b True *Main> a `equal` c False
热身完毕,接下来我们将要开始讲Monoid这个 class
。
Monoid
在讲述 Monoid
之前,我们需要先看看 Functor
和 Applicative
热热身。
Functor和Applicative
上定义:
Prelude> :i Functor class Functor (f :: * -> *) where fmap :: (a -> b) -> f a -> f b (<$) :: a -> f b -> f a {-# MINIMAL fmap #-} -- Defined in ‘GHC.Base’ instance Functor (Either a) -- Defined in ‘Data.Either’ instance Functor [] -- Defined in ‘GHC.Base’ instance Functor Maybe -- Defined in ‘GHC.Base’ instance Functor IO -- Defined in ‘GHC.Base’ instance Functor ((->) r) -- Defined in ‘GHC.Base’ instance Functor ((,) a) -- Defined in ‘GHC.Base’ Prelude> :i Applicative class Functor f => Applicative (f :: * -> *) where pure :: a -> f a (<*>) :: f (a -> b) -> f a -> f b GHC.Base.liftA2 :: (a -> b -> c) -> f a -> f b -> f c (*>) :: f a -> f b -> f b (<*) :: f a -> f b -> f a {-# MINIMAL pure, ((<*>) | liftA2) #-} -- Defined in ‘GHC.Base’ instance Applicative (Either e) -- Defined in ‘Data.Either’ instance Applicative [] -- Defined in ‘GHC.Base’ instance Applicative Maybe -- Defined in ‘GHC.Base’ instance Applicative IO -- Defined in ‘GHC.Base’ instance Applicative ((->) a) -- Defined in ‘GHC.Base’ instance Monoid a => Applicative ((,) a) -- Defined in ‘GHC.Base’
fmap
?似曾相识,不就是map吗?map是函数,但是 Functor
是能应用到map函数
的东西抽象出来的接口。我们暂且把被map应用的东西叫做 "容器" 或者 "盒子"。
比如最典型的, List
能被map(之后的例子我们都用List):
Prelude> map (+1) [1 .. 3] [2,3,4]
<$
?看样子是指把一个类型放到容器里,便能反推出该类型所对应的有容器的值,
说起来好绕,看一个具体例子好了:
Prelude> fmap (+1) [1 .. 3] [2,3,4] Prelude> (<$) 1 [3] [1]
果然是这样。瞧,这就是类型声明的好处,读代码靠看类型就能猜个大概出来 :joy:
至于 Applicative
,我们则可以看到,首先Applicative是Functor,此外:
class Functor f => Applicative (f :: * -> *) where pure :: a -> f a (<*>) :: f (a -> b) -> f a -> f b GHC.Base.liftA2 :: (a -> b -> c) -> f a -> f b -> f c (*>) :: f a -> f b -> f b (<*) :: f a -> f b -> f a
pure <*> *> <* GHC.Base.liftA2
是不是更晕?没关系,Haskell就是这样,每一行代码你都需要仔细考虑。我们对这上面的讲解分别看下面五个例子:
Prelude> pure 1 :: [Int] [1] Prelude> (<*>) [\x -> x + 1] [1] [2] Prelude> (*>) [1] [2] [2] Prelude> (<*) [1] [2] [1] Prelude> GHC.Base.liftA2 (\a b -> a + b) [1] [2] [3]
瞧,有感觉了吗?如果没有的话,我想可能需要重新一步一步跟着来,再读一遍此前的内容。接下来我们看 Monoid
。
Monoid
首先我们打开ghci看看定义:
Prelude> :m Data.Monoid Prelude Data.Monoid> :i Monoid class Monoid a where mempty :: a mappend :: a -> a -> a mconcat :: [a] -> a {-# MINIMAL mempty, mappend #-} -- Defined in ‘GHC.Base’ instance Num a => Monoid (Sum a) -- Defined in ‘Data.Monoid’ instance Num a => Monoid (Product a) -- Defined in ‘Data.Monoid’ instance Monoid (Last a) -- Defined in ‘Data.Monoid’ instance Monoid (First a) -- Defined in ‘Data.Monoid’ instance Monoid (Endo a) -- Defined in ‘Data.Monoid’ instance Monoid a => Monoid (Dual a) -- Defined in ‘Data.Monoid’ instance Monoid Any -- Defined in ‘Data.Monoid’ instance GHC.Base.Alternative f => Monoid (Alt f a) -- Defined in ‘Data.Monoid’ instance Monoid All -- Defined in ‘Data.Monoid’ instance Monoid [a] -- Defined in ‘GHC.Base’ instance Monoid Ordering -- Defined in ‘GHC.Base’ instance Monoid a => Monoid (Maybe a) -- Defined in ‘GHC.Base’ instance Monoid a => Monoid (IO a) -- Defined in ‘GHC.Base’ instance Monoid b => Monoid (a -> b) -- Defined in ‘GHC.Base’ instance (Monoid a, Monoid b, Monoid c, Monoid d, Monoid e) => Monoid (a, b, c, d, e) -- Defined in ‘GHC.Base’ instance (Monoid a, Monoid b, Monoid c, Monoid d) => Monoid (a, b, c, d) -- Defined in ‘GHC.Base’ instance (Monoid a, Monoid b, Monoid c) => Monoid (a, b, c) -- Defined in ‘GHC.Base’ instance (Monoid a, Monoid b) => Monoid (a, b) -- Defined in ‘GHC.Base’ instance Monoid () -- Defined in ‘GHC.Base’
class Monoid a where mempty :: a mappend :: a -> a -> a mconcat :: [a] -> a
我们看类型来推测:
mempty mappend mconcat
说实话,这些都是我猜的,所以得验证一下:
Prelude Data.Monoid> mempty :: [Int] [] Prelude Data.Monoid> mappend [1] [2] [1,2] Prelude Data.Monoid> mconcat [[1], [2, 3], [4]] [1,2,3,4]
原来是这样,第一个如我所说,第二个是把两个容器连起来,第三个是把容器的容器打散组合成
一个新的容器。原来是这样,那么有哪些类型实现了这个接口呢?我们可以看到上面, Maybe a
, [a]
等都实现了,因为 Monoid
是针对操作容器自身的,所以感觉有些抽象,有点像 Python
里的 metaclass
。这一节得要仔细消化。
Monad
Monad和Monoid有什么关系吗?说实话,我个人认为是雷锋和雷峰塔的关系。国际惯例,我们来看看定义:
Prelude> :i Monad class Applicative m => Monad (m :: * -> *) where (>>=) :: m a -> (a -> m b) -> m b (>>) :: m a -> m b -> m b return :: a -> m a fail :: String -> m a {-# MINIMAL (>>=) #-} -- Defined in ‘GHC.Base’ instance Monad (Either e) -- Defined in ‘Data.Either’ instance Monad [] -- Defined in ‘GHC.Base’ instance Monad Maybe -- Defined in ‘GHC.Base’ instance Monad IO -- Defined in ‘GHC.Base’ instance Monad ((->) r) -- Defined in ‘GHC.Base’ instance Monoid a => Monad ((,) a) -- Defined in ‘GHC.Base’
我们继续猜测Monad的几个接口:
return fail >>= >>
看看具体例子:
Prelude> return 1 :: [Int] [1] Prelude> fail "hello" :: [Int] [] Prelude> (>>=) [1] (\x -> [x + 1]) [2] Prelude> (>>) [1] [2] [2]
没了,这就是Monad。更深的Monad的内容需要到以后实际用到才更好理解。这里先讲到这个程度。
为什么总是讲到盒子?容器?抽象?
我们很久之前就讲到过抽象的好处,抽象使得我们不必关心具体实现细节,只需要知道有这么一个 方法,我们只要这样用就好。而所谓的盒子,所谓的容器其实是同样的想法,为了抽象。
什么是Monad?实现了这几个接口就可以是一个Monad。 XMonad 就因此得名, 因为他把核心实现了Monad这个接口(类型类)。
以上所述就是小编给大家介绍的《Haskell简明教程(四):Monoid, Applicative, Monad》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Is Parallel Programming Hard, And, If So, What Can You Do About
Paul E. McKenney
The purpose of this book is to help you understand how to program shared-memory parallel machines without risking your sanity.1 By describing the algorithms and designs that have worked well in the pa......一起来看看 《Is Parallel Programming Hard, And, If So, What Can You Do About 》 这本书的介绍吧!