Haskell : Functionally Solving Problems 部分学习笔记

来源:互联网 发布:特别搞笑的网络歌曲 编辑:程序博客网 时间:2024/05/17 22:24

Functionally Solving Problems

小tips:
1. fmap 是各个地方都写好的了。
2. Maybe 'a' 的也算是一个装有东西的 functor.
3. 你可以把 fmap 想做是一个函数,他接受另一个函数跟一个 functor,然后把函数对 functor 每一个元素做映射,或你可以想做他是一个函数,他接受一个函数并把他 lift 到可以在 functors 上面操作。两种想法都是正确的,而且在 Haskell 中是等价。
4. functor 感觉很像是一个 function, 但是是一个有 context 的function (context 如“盒子”,如一层抽象的“壳”),就像是一个比 function 更高级、抽象的东西.
5. 要记住型别变量跟参数的名字还有值绑定的名称不冲突。

  • functor laws

    1. functor law 的第一条说明,如果我们对 functor 做 map id ,那得到的新的 functor 应该要跟原来的一样。如果写得正式一点,他代表fmap id = i 。基本上他就是说对 functor 调用fmapid ,应该等同于对 functor 调用 id 一样。毕竟 id 只是 identity function,他只会把参数照原样丢出。他也可以被写成\x -> x 。如果我们对 functor 的概念 就是可以被 map over 的对象,那fmap id = id 的性就显而易见。

    2. 第二定律描述说先将两个函数合成并将结果 map over 一个 functor 的结果,应该跟先将第一个函数 map over 一个 functor,再将第二个函数 map over 那个 functor 的结果是一样的。正式地写下来的话就是fmap (f . g) = fmap f . fmap g 。或是用另外一种写法,对于任何一个 functor F,下面这个式子应该要被遵守:fmap (f . g) F = fmap f (fmap g F)

  • 理解 functor 的机制
    可以用一个多参数的函数来 map functor,我们会得到一个包含了函数的 functor。那现在我们能对这个包含了函数的 functor 做什么呢?我们能用一个吃这些函数的函数来 map over 这个 functor,这些在 functor 中的函数都会被当作参数丢给我们的函数。(functor 像是一个拿着你的快递的快递员,但是在使用时,快递员本身是隐形的;只是在型别判断时,不能将拿着你的快递的快递员你的快递混淆)

   ghci> let a = fmap (*) [1,2,3,4]   ghci> :t a   a :: [Integer -> Integer]   ghci> fmap (\f -> f 9) a   [9,18,27,36]
  • Applicative functors(以 Maybe 作例子)

Applicative 这个 typeclass。他位在 Control.Applicative 中,在其中定义了两个函数 pure 跟 <*> 。他并没有提供缺省的实作,如果我们想使用他必须要为他们 applicative functor 的实作。typeclass 定义如下:

    class (Functor f) => Applicative f where    pure :: a -> f a    (<*>) :: f (a -> b) -> f a -> f b

由上面三行我们能够知道下面几点:

  1. 描述了一个型别构造子要是 Applicative ,他必须也是 Functor 。这就是为什么我们说一个型别构造子属于 Applicative 的话,他也会是 Functor ,因此我们能对他使用 fmap 。
  2. pure 。他的型别宣告是 pure :: a -> f a 。pure 应该要接受一个值,然后回传一个包含那个值的 applicative functor。对于 pure 比较好的说法是把一个普通值放到一个缺省的 context 下,一个最小的 context 但仍然包含这个值。
  3. <*> 也非常有趣。他的型别是 f (a -> b) -> f a -> f b 。可以联想到fmap :: (a -> b) -> f a -> f b 。他有点像加强版的 fmap。然而 fmap 接受一个函数跟一个 functor,然后套用 functor 之中的函数。<*> 则是接受一个装有函数的 functor 跟另一个 functor,然后取出第一个 functor 中的函数将他对第二个 functor 中的值做 map。

下面两种效果相同

ghci> Just (+3) <*> Just 9Just 12ghci>ghci> fmap (+3) (Just 9)Just 12

函数 <$> ,他基本上就是中缀版的 fmap 。他是这么被定义的:

(<$>) :: (Functor f) => (a -> b) -> f a -> f b  f <$> x = fmap f x

<$> 的使用显示了 applicative style 的好处。如果我们想要将 f 套用三个 applicative functor。我们可以写成 f <$> x <> y <> z 。如果参数不是 applicative functor 而是普通值的话。我们则写成 f x y z 。

  • 其他 Applicative 的 instance
    1. IO instance.实现:
 instance Applicative IO where      pure = return      a <*> b = do          f <- a          x <- b          return (f x) 

如果我们再使用盒子的模拟,我们可以把 getLine 想做是一个去真实世界中拿取字串的盒子。而 (++) <$> getLine <*> getLine 会创造一个比较大的盒子,这个大盒子会派两个盒子去终端拿取字串,并把结果串接起来放进自己的盒子中。

 myAction :: IO String   myAction = do      a <- getLine      b <- getLine      return $ a ++ b 

 myAction :: IO String   myAction = (++) <$> getLine <*> getLine 

效果相同。
2. ZipList instance 的实现: (包含在 Control.Applicative )

instance Applicative ZipList where          pure x = ZipList (repeat x)          ZipList fs <*> ZipList xs = ZipList (zipWith (\f x -> f x) fs xs)

...后面还有一些些...工程使用再算

0 0
原创粉丝点击