前言
ReactiveCocoa是一个FRP的思想在Objective-C中的实现框架。FRP即Functional Reactive Programming。
ReactiveCocoa 是基于函数式编程中一个叫Monad概念。这篇文章主要讲什么是Monad。
文中的代码以Haskell演示。
提及Monad通常会涉及到Functor, Applicative 他们三个经常一起出现,我们将依此讲解。
函数式编程中的一些概念背景知识
Typeclass
Typeclass 类似于Java中的接口,OC中的协议。
Typeclass 中定义了一些函数,实现一个typeclass就是要实现这些函数。
所有实现了这个typeclass的数据类型都拥有这些共同的行为。
定义Typeclass
class Eq a where
(==) :: a -> a -> Bool
(/=) :: a -> a -> Bool
x == y = not (x /= y)
x /= y = not (x == y)
定义一个叫做Eq的typeclass。 a 一个型别变量,他代表a是任何我们在定义instance时的型别。然后又定义了4个函数。
Maybe
- Maybe类型封装了一个可选值,一个 Maybe a 类型的值要么包含一个a 类型的值(Just a),要么为空(Nothing表示)。
- Maybe看作一个上下文,这个上下文表示某次计算可能成功,也可能失败,成功是用(Just a)表示,失败时用Nothing表示。
提出问题
- 假设有一个值2, 如何将函数(+3) 应用到这个值上?
幼儿园加法,大家都知道吧。 - 如果值2是一个上下文值呢?
那就需要使用Functor。
Functor typeclass
//1. Functor typeclass
class Functor f where
fmap :: (a->b)->f a -> f b
//2. Maybe Functor (即假设f 是Maybe)
class Functor Maybe where
fmap :: (a ->b) -> Maybe a -> Maybe b
//3. Maybe Functor 实现
instance Functor Maybe where
fmap func (Just x) = Just (func x)
fmap func Nothing = Nothing
//4. Haskell 运行效果
*Main> fmap (+3) Nothing
Nothing
*Main> fmap (+3) (Just 2)
Just 5
注释: 方法fmap有两个参数:
参数一:(a->b) 表示一个函数,函数的需要一个参数,输入运行结果b
参数二:f a 其中f是参数a的类型限制。比如:假如f 是Maybe, 则a 要么是Nothing, 要么是Just a
在 3 instance 中运用了模式匹配,当参数为Just x 时调用第一行。参数为Nothing时调用第二行。
问题又来了
- 如果函数(+3) 也在上下文中呢? 比如Maybe,此时(+3) 就变成了Just(+3)
那就需要Applicative。
Applicative typeclass
//1. Applicative typeclass
class Functor f => Applicative f where
pure :: a - > f a
(<*>) :: f (a -> b) -> f a -> f b
//2. Maybe Applicative
class Functor Maybe => Applicative Maybe where
pure :: a -> Maybe a
(<*>) :: Maybe (a -> b) -> Maybe a -> Maybe b
//3. Maybe Applicative 实现
instance Applicative Maybe where
pure :: Just
Nothing <*> _ = Nothing
(Just func) <*> something = fmap func something
//4. Haskell 运行效果
*Main> (<*>) (Just (+3)) (Just 2)
Just 5
*Main> (<*>) (Just (+3)) Nothing
Nothing
注释:=> 符号解释: =>左边的部分叫型别约束。即 Applicative 中 f 的型别是Functor
总结一下
Fouctor: 应用一个函数到一个上下文中的值,返回一个上下文中的值。
Applicative: 应用一个上下文中函数到一个上下文中的值,返回一个上下文中的值。
那么Monad是干什么呢
Monad:应用一个函数到一个上下文的值。并返回一个在上下文的值的函数。
//1. Monad classtype
class Applicative m => Monad m where
return :: a -> m a
(>>=) :: m a -> m b -> m b
//2. Maybe Monad
class Applicative Maybe => Monad Maybe where
return :: a -> Maybe a
>>= :: Maybe a -> (a -> Mayb b) -> Maybe b
//3. Maybe Monad 实现
instance Monad Maybe where
return x = Just x
Nothing >>= func = Nothing
Just x >>= func = func x
//4. Haskell 运行效果
//4.1 定义一个函数
half x = if even x
then Just (x `div` 2)
else Nothing
//4.2 运行效果
*Main> Just 20 >>= half
Just 10
*Main> Just 10 >>= half
Just 5
*Main> Just 5 >>= half
Nothing
//4.3 链式操作
*Main> Just 20 >>= half >>= half >>= half
Nothing
三者之间的联系
Applicative是增强型的Functor。
Monad是增强型的Applicative。
再总结一下
一个Functor 就是实现了Functor typeclass 的数据类型。
一个Applicative 就是现实了 Applicative typeclass 的数据类型。
一个Monad 就是实现了Monad typeclass的数据类型。
ReactiveCocoa
开头讲了ReactiveCocoa是基于函数式响应式的编程思想。基于Monad的思想。我们具体来看一下。
我们知道,RAC的核心概念是Signal而Signal继承自 RACStream,看一下RACStream源码:
@interface RACStream : NSObject
+ (instancetype)empty;
// 主要看这里
+ (instancetype)return:(id)value;
- (instancetype)bind:(RACStreamBindBlock (^)(void))block;
- (instancetype)concat:(RACStream *)stream;
- (instancetype)zipWith:(RACStream *)stream;
@end
我们可以看到 RACStream 中有两个方法
- + (instancetype)return:(id)value;
- - (instancetype)bind:(RACStreamBindBlock (^)(void))block;
return:方法的功能就是将一个值 value放入 RACStream上下文中;
bind:方法的功能则是将一个 RACStreamBindBlock类型的 block应用到一个在 RACStream上下文中的值(receiver),并返回另一个在 RACStream
上下文中的值。
注,RACStreamBindBlock类型的 block就是一个接收一个普通值 value但是返回一个在 RACStream上下文中的值的“函数”;
RACStream与Monad 比较一下
// 1. Monad 定义
class Applicative m => Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
//2. 假设m 是RACStream 类型
class Applicative RACStream => Monad RACStream where
return :: a -> RACStream a
(>>=) :: RACStream a -> (a -> RACStream b) -> RACStream b
其中:
return :: a -> RACStream a就对应 + (instancetype)return:(id)value;
(>>=) :: RACStream a -> (a -> RACStream b) -> RACStream b
则对应 - (instancetype)bind:(RACStreamBindBlock (^)(void))block;
是不是一样一样的。
参考资料:
http://blog.leichunfeng.com/blog/2015/11/08/functor-applicative-and-monad/
《Haskell趣学指南》