应用的常见实例
也许
Maybe
是一个包含可能不存在的值的应用函子。
instance Applicative Maybe where
pure = Just
Just f <*> Just x = Just $ f x
_ <*> _ = Nothing
pure
通过向其应用 Just
将给定值提升到 Maybe
。(<*>)
函数将包含在 Maybe
中的函数应用于 Maybe
中的值。如果函数和值都存在(使用 Just
构造),则将该函数应用于该值并返回包装结果。如果缺少任何一个,则计算无法继续,而是返回 Nothing
。
清单
列表适合类型签名 <*> :: [a -> b] -> [a] -> [b]
的一种方法是采用两个列表’笛卡尔积,将第一个列表的每个元素与第二个列表中的每个元素配对:
fs <*> xs = [f x | f <- fs, x <- xs]
-- = do { f <- fs; x <- xs; return (f x) }
pure x = [x]
这通常被解释为模仿非确定性,其中一个值列表代表一个非确定性值,其值可能超出该列表; 因此,两个非确定性值的组合会覆盖两个列表中值的所有可能组合:
ghci> [(+1),(+2)] <*> [3,30,300]
[4,31,301,5,32,302]
无限流和压缩列表
有一类 Applicative
s 将它们的两个输入压缩在一起。一个简单的例子是无限流:
data Stream a = Stream { headS::a, tailS::Stream a }
Stream
的 Applicative
实例将点函数流应用于点参数流,按位置对两个流中的值进行配对。pure
返回一个常量流 - 一个固定值的无限列表:
instance Applicative Stream where
pure x = let s = Stream x s in s
Stream f fs <*> Stream x xs = Stream (f x) (fs <*> xs)
列表也承认了一个 zippy``Applicative
实例,其中存在 ZipList
newtype:
newtype ZipList a = ZipList { getZipList :: [a] }
instance Applicative ZipList where
ZipList xs <*> ZipList ys = ZipList $ zipWith ($) xs ys
由于 zip
根据最短的输入修剪其结果,因此满足 Applicative
定律的 pure
的唯一实现是返回无限列表的实现:
pure a = ZipList (repeat a) -- ZipList (fix (a:)) = ZipList [a,a,a,a,...
例如:
ghci> getZipList $ ZipList [(+1),(+2)] <*> ZipList [3,30,300]
[4,32]
这两种可能性提醒我们外部和内部产品,类似于在第一种情况下将 1 列(n x 1
)矩阵与 1 行(1 x m
)矩阵相乘,从而获得 n x m
矩阵(但是扁平化); 或者在第二种情况下乘以 1 行和 1 列矩阵(但没有求和)。
功能
当专门用于 (->) r
时,pure
和 <*>
的类型签名分别与 K
和 S
组合的类型签名:
pure::a -> (r -> a)
<*> :: (r -> (a -> b)) -> (r -> a) -> (r -> b)
pure
必须是 const
,<*>
接受一对函数并将它们分别应用于固定参数,应用两个结果:
instance Applicative ((->) r) where
pure = const
f <*> g = \x -> f x (g x)
函数是典型的 zippy
应用程序。例如,由于无限流与 (->) Nat
同构,…
-- | Index into a stream
to::Stream a -> (Nat -> a)
to (Stream x xs) Zero = x
to (Stream x xs) (Suc n) = to xs n
-- | List all the return values of the function in order
from :: (Nat -> a) -> Stream a
from f = from' Zero
where from' n = Stream (f n) (from' (Suc n))
…以更高阶的方式表示流自动生成 zippy Applicative
实例。