import UIKit
import Foundation
/***********Functor(函子)、Monad(单子)、Applicative(高级函子)****************/
/*
Context(上下文)呢,我们可以将它理解为对值的一个包装,通过这层包装,我们可以得知值此时所处在的一个状态
*/
enum Result {
case success(T)
case failure(String)
}
/// Functor 函子
/// 一个函数只能作用于它声明好的特定类型的值,运算整形的函数不能用来运算一个非整形的Context
func double(_ value: Int) -> Int {
return 2 * value
}
let a = 2
let b = double(a)
/// Functor : 使一个只能运算值的函数用来运算一个包有这个值类型的Context,最后返回的一个包有运算结果的Context
/// Context(结果值) = map(Context(初始值), 运算函数)
/// 自定义运算符 https://www.jianshu.com/p/f664b4aa5548
/// 定义优先级组ChainingPrecedence,设置结合性为left,优先级大于TernaryPrecedence
/// 定义运算符 <^>,设置优先级别为ChainingPrecedence
/// 扩展Result,添加map运算。
precedencegroup ChainingPrecedence {
associativity: left
higherThan: TernaryPrecedence
}
// Functor
infix operator <^> : ChainingPrecedence
// For Result
func <^><T, O>(lhs: (T) -> O, rhs: Result) -> Result {
return rhs.map(lhs)
}
// extension
extension Result {
func map(_ mapper: (T) -> O) -> Result {
switch self {
case .failure(let error):
return .failure(error)
case .success(let value):
return .success(mapper(value))
}
}
}
let a1: Result = .success(2)
let b1 = double <^> a1
/// Swift的数组也可以当成是Context,它是作为一个包有多个值的状态存在
let arrA = [1, 2, 3, 4, 5]
let arrB = arrA.map(double)
/// RxSwift
/// let ob = Observable.just(1).map(double)
/// Applicative
/*
Applicative其实就是高级的Functor,我们可以调出上面Functor的map伪代码:
Context(结果值) = map(Context(初始值), 运算函数)
在函数式编程中,函数也可以作为一个值来看待,若此时这个函数也是被一个Context包裹的,单纯的map是不能接受包裹着函数的Context,所以我们引入了Applicative:
Context(结果值) = apply(Context(初始值), Context(运算函数))
*/
extension Result {
func apply(_ mapper: Result<(T) -> O>) -> Result {
switch mapper {
case .failure(let error):
return .failure(error)
case .success(let function):
return self.map(function)
}
}
}
// Applicative
infix operator <*> : ChainingPrecedence
// For Result
func <*><T, O>(lhs: Result<(T) -> O>, rhs: Result) -> Result {
return rhs.apply(lhs)
}
let function: Result<(Int) -> Int> = .success(double)
let a2: Result = .success(2)
let b2 = function <*> a2
/// Applicative在日常开发中其实用的不多,很多时候我们并不会将一个函数塞进一个Context上,但是如果你用了一些略为高阶的函数时,它强劲的能力就能在此时表现出来
/// func haha(a: Int, b: Int, c: Int) -> Int,通过函数柯里化我们可以将其转化为(Int) -> (Int) -> (Int) -> Int类型的函数。我们此时将Person的构造器进行函数柯里化:curry(Person.init),此时我们得到的是类型为(String) -> (Int) -> (String) -> Person的值
//func parse(jsonObject: Any, key: String) -> Result;
/// 将一个JSON数据解析成Person的实例,以一个Result的包装返回,如果解析失败,Result处理失败状态会携带一个错误的实
struct Person {
let name: String
let age: Int
let from: String
}
//func parseJSONToPerson(json: Any) -> Result {
// return curry(Person.init)
// <^> parse(jsonObject: json, key: “name”)
// <> parse(jsonObject: json, key: “age”)
// <> parse(jsonObject: json, key: “from”)
//}
/// 首先通过函数的柯里化我们得到了类型为(String) -> (Int) -> (String) -> Person的值,它也是一个函数,然后经过了<^>map的操作,map的右边是一个解析了name返回的Result,它的类型为Result,map将函数(String) -> (Int) -> (String) -> Person应用于Result,此时我们得到的是返回的结果(Int) -> (String) -> Person的Result包装:Result<(Int) -> (String) -> Person>(因为已经消费掉了一个参数),此时,这个函数就被一个Context包裹住了,后面我们不能再用map去将这个函数应用在接下来解析出来的数据了,所以这是我们就借助于Applicative的<>,接下来看第二个参数,parse函数将JSON解析返回了类型为Result的结果,我们通过<>将Result<(Int) -> (String) -> Person>的函数取出来,应用于Result,就得到了类型为Result<(String) -> Person>的结果。以此类推,最终我们就获取到了经JSON解析后的结果Result。Applicative强大的能力能够让代码变得如此优雅,这就是函数式编程的魅力之所在。
/// Monad 单子
/// Monad需要实现的函数我们可以称为bind
/// function :: 值A -> Context(值B)(值A与值B的类型可相同亦可不同)我们的bind函数就可以这么写了:Context(结果值) = Context(初始值) >>- function
/// 简单说,Monad就是一种设计模式,表示将一个运算过程,通过函数拆解成互相连接的多个步骤。你只要提供下一步运算所需的函数,整个运算就会自动进行下去。
/// http://www.ruanyifeng.com/blog/2015/07/monad.html
/*
let ob = Observable.just(1).flatMap { num in
Observable.just(“The number is (num)”)
}
*/
/// Context(结果值) = Context(初始值) >>- function
extension Result {
func flatMap(_ mapper: (T) -> Result) -> Result {
switch self {
case .failure(let error):
return .failure(error)
case .success(let value):
return mapper(value)
}
}
}
// Monad
infix operator >>- : ChainingPrecedence
// For Result
func >>-<T, O>(lhs: Result, rhs: (T) -> Result) -> Result {
return lhs.flatMap(rhs)
}
/*
通过特定条件进行本地数据库的查询,找出相关的数据
利用上面从数据库得到的数据作为参数,向服务器发起请求,获取响应数据
将从网络获取到的原始数据转换成JSON数据
将JSON数据进行解析,返回最终解析完成的有特定类型的实体
*/
/*
// A代表从数据库查找数据的条件的类型
// B代表期望数据库返回结果的类型
func fetchFromDatabase(conditions: A) -> Result { … }
// B类型作为网络请求的参数类型发起网络请求
// 获取到的数据为C类型,可能是原始字符串或者是二进制
func requestNetwork(parameters: B) -> Result { … }
// 将获取到的原始数据类型转换成JSON数据
func dataToJSON(data: C) -> Result { … }
// 将JSON进行解析输出实体
func parse(json: JSON) -> Result { … }
*/
/*
var entityResult: Entity?
if case .success(let b) = fetchFromDatabase(conditions: XXX) {
if case .success(let c) = requestNetwork(parameters: b) {
if case .success(let json) = dataToJSON(data: c) {
if case .success(let entity) = parse(json: json) {
entityResult = entity
}
}
}
}
*/
/// let entityResult = fetchFromDatabase(conditions: XXX) >>- requestNetwork >>- dataToJSON >>- parse
/*
// B类型作为网络请求的参数类型发起网络请求
// 获取到的数据为C类型,可能是原始字符串或者是二进制
func requestNetwork(urlString: String) -> (B) -> Result {
return { parameters in
return { … }
}
}
*/
/// let entityResult = fetchFromDatabase(conditions: XXX) >>- requestNetwork(urlString: “XXX.com/XXX/XXX”) >>- dataToJSON >>- parse
/// 对一系列针对值与Context的操作进行链式结合,代码极其优雅,清晰明了。将值与Context之间的转换、Context内部进行的操作对外屏蔽,像上面我用原始的方式进行操作,我们需要手动地分析Context的情况,手动地针对不同的Context状态进行相应的操作,而如果我们使用Monad,整一流程下来我们什么都不需要做,坐享其成,取得最终的结果。