Functor(函子)、Monad(单子)、Applicative(高级函子)

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,整一流程下来我们什么都不需要做,坐享其成,取得最终的结果。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值