ReactiveCocoa 与函数式编程有什么关系?

前言

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

  1. Maybe类型封装了一个可选值,一个 Maybe a 类型的值要么包含一个a 类型的值(Just a),要么为空(Nothing表示)。
  2. Maybe看作一个上下文,这个上下文表示某次计算可能成功,也可能失败,成功是用(Just a)表示,失败时用Nothing表示。

提出问题

  1. 假设有一个值2, 如何将函数(+3) 应用到这个值上?
    幼儿园加法,大家都知道吧。
  2. 如果值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时调用第二行。

问题又来了

  1. 如果函数(+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趣学指南》

你可能感兴趣的:(ReactiveCocoa 与函数式编程有什么关系?)