Swift 函数式编程探索(2)——实践中使用 Functor 和 Monad

上次了解了一点函数式编程之后,学习了一下《Funtional Swift》 这本书,仿佛打开了新世界的大门。
一直看文章不如自己实践,于是我尝试在项目中使用了一下,现在对 monad、functor 等已经相对熟悉理解了,并且发现这个范式有着非常方便的一面。
这里用到的代码我都写在了 playground 里,放到了 Github 上。

这里就分享一下我在最近的实际项目中是如何使用函数式编程的:

1. 使用 monad(flagMap)处理异常

上一篇函数式编程中说到,定义一个 functor 和 monad 非常容易,只要定义了 map 函数和 flatMap 函数就可以。
使用 flatMap 有非常方便的错误处理能力,比如说使用 monad Result:

public enum Result { 
    case success(Value)
    case failure(ErrorType)

    public func flatMap( @noescape transform: Value throws -> Result) rethrows -> Result {
        switch self {
        case let .failure(error):
            return .failure(error)
        case let .success(value):
            return try transform(value)
        }
    }
}

假设我们要将一个从 urlsession 获得的data 数据转换为 json 格式,我们定义一个从 nsdata 转为 json 的的方法

//注意这里使用了 type alias ,较为方便
typealias JSON = [String : AnyObject]
func toJSON(data: NSData) -> Result<[String : AnyObject]> {
    do {
        if let json = try NSJSONSerialization.JSONObjectWithData(
            data, options: []) as? JSON  {
            return .success(json)
        }
        return .success([ : ])
    } catch let error {
        return .failure(error)
    }
}

当我们获得了一个类型为 Result 的result时候,我们可以这样:

let json = result.flatMap(form)
//如果成功,json 包含 JSON,如果失败,则是包含 Error

还可以更方便一点,我们使用上一篇定义的操作符>>-
我们就能这么写:

let json = result >>= form

不过,我们有可能会经常在一个形如someF(a : U) -> Result中重复写类似 form 函数一样的do catch。
所以有没有更好的解决办法?

2. 使用 functor(map)处理异常

其实也是有的,我们使用map。

extension Result{

    public func map( @noescape transform: Value throws -> T) rethrows -> Result {
        switch self {
        case let .failure(error):
            return Result.failure(error)
        case let .success(value):
            return try Result.success(transform(value))
        }
    }

    //因为已经有了一个 rethrows 的 map,所以这里不能取名 map,使用 mapError
    public func mapError( @noescape transform: Value throws -> T) -> Result {
        do { 
            return try self.map(transform)
        } catch let error {
            return .failure(error)
        }
    }
}

我们写一个会抛出异常的函数,这么写,直接将之抛出:

public func toJSONThrows(data: NSData) throws -> JSON {
    if let json = try NSJSONSerialization.JSONObjectWithData(
        data, options: []) as? JSON {
        return json
    }
    return [ : ]
}

当我们获得了一个类型为 Result 的result时候,我们可以这样:

let json = result.mapError(toJSONThrows)
//如果成功,json 包含 JSON,如果失败,则是包含 Error,和faltMap一样

这样一来,任何可能会抛出错误的函数都使用 mapError,就能很方便地进行错误处理了。

你可能感兴趣的:(Swift 函数式编程探索(2)——实践中使用 Functor 和 Monad)