Functor, Applicative, Monad片面理解

導讀

判斷一個語言原生是否支持函數(shù)式編程,主要看它的函數(shù)是否first-class function,函數(shù)作為第一對象。

類型:規(guī)定了變量可以取的值得范圍,以及該類型的值可以進行的操作。根據(jù)類型的值的可賦值狀況,可以把類型分為三類:
1、一級的(first class)。該等級類型的值可以傳給子程序作為參數(shù),可以從子程序里返回,可以賦給變量。大多數(shù)程序設計語言里,整型、字符類型等簡單類型都是一級的。
2、二級的(second class)。該等級類型的值可以傳給子程序作為參數(shù),但是不能從子程序里返回,也不能賦給變量。
3、三級的(third class)。該等級類型的值連作為參數(shù)傳遞也不行。

OC并不是函數(shù)式語言,但通過擴展可以讓其支持一些函數(shù)式語言的特性。如ReactiveCocoa,大量的使用Block來實現(xiàn)函數(shù)式語言的特點。

與OC不同的是,Swift可以說是函數(shù)式語言,它的func是第一公民。

// 為變量賦值
var aFunc = someFunc

// 作為函數(shù)輸入
func inputFunc(_ f: ()->Void) { f() }

// 作為函數(shù)輸出
func outputFunc() -> () -> Void { return someFunc }

Functor

來自Wiki的解釋

In mathematics, a functor is a type of mapping between categories which is applied in category theory. Functors can be thought of as homomorphisms between categories. In the category of small categories, functors can be thought of more generally as morphisms.

這是數(shù)學上的解釋,比較難懂。但與編程上還是有一些區(qū)別的。

Functor一般定義:

  • 應用在一個“包裝過”的值V上
  • 接受一個參數(shù),該參數(shù)是一個普通函數(shù),接收一個普通值并返回一個普通值

在Swift里Functor可以看作是一種數(shù)據(jù)類型,該類型的特征是有一個map函數(shù),例如Optional。Optional的map函數(shù)接受一個(T)->U并返回一個u?從結果來分析,Optional的map函數(shù)對Self進行了解包,如果有值則將輸入的函數(shù)應用到這個值上并產(chǎn)生一個新值,最后這個值經(jīng)過包裝后返回。

let a = Optional(10)
let b = a.map { $0 + 1 }
print(a, b)
// Optional(10) Optional(11)

除了Optional,Array也是一個Functor。

let a = [1, 2, 3, 4]
let b = a.map{ "\($0)" }
print(a, b)
// [1, 2, 3, 4] ["1", "2", "3", "4"]

另外RACSignal也是一個Functor,可以將它看作是一個數(shù)據(jù)類型,它包裝了一個next和一個error值。有趣的是RACSignal的值是被定義為未來將要產(chǎn)生的。

Applicative

和Functor不同的是,Applicative是將一個包裝后的函數(shù)應用在一個包裝后的值上。

Applicative一般定義

  • 應用在一個“包裝過”的值V上
  • 輸入一個參數(shù),該參數(shù)是一個“包裝過后”的普通函數(shù)

Swift里并沒有實現(xiàn)Applicative的類,但可以簡單擴展一下。

extension Optional {
    func apply<U>(f: ((Wrapped) -> U)?) -> U? {
        switch f {
        case .some(let someF): return self.map(someF)
        case .none: return .none
        }
    }
}

extension Array {
    func apply<U>(fs: [(Element) -> U]) -> [U] {
        var result = [U]()
        for f in fs {
            for element in self.map(f) {
                result.append(element)
            }
        }
        return result
    }
}

對于Optional來說添加Applicative的支持意義不是很大。但對于數(shù)組來說是一個不錯的擴展。apply函數(shù)先歷遍傳入的函數(shù)數(shù)組,然后將每個函數(shù)都應用到數(shù)組的每個值上。

let a = [1, 2, 3]

func plusOne(_ a: Int) ->Int {
    return a + 1
}

func plusTwo(_ a: Int) ->Int {
    return a + 2
}

func plusThree(_ a: Int) ->Int {
    return a + 3
}

let b = a.apply(fs: [plusOne, plusTwo, plusThree])

print(b)
// [2, 3, 4, 3, 4, 5, 4, 5, 6]


Monad

對于學RAC的人來說,了解Monad的概念是非常重要的。Monad的一個標識是實現(xiàn)Bind或叫flatMap的函數(shù)。

Monad的一般定義:

  • 應用在一個“包裝過”的值V上
  • 傳入一個參數(shù),該參數(shù)是一個函數(shù),該函數(shù)接受一個普通值并返回一個“包裝”過的值

Swift里的Optional是這么定義flatMap的

public func flatMap<U>(_ transform: (Wrapped) throws -> U?) rethrows -> U?

Optional的map和flatMap區(qū)別并不是很大,但考慮一下下面的場景。在Optional上用map有機會產(chǎn)生嵌套的Optional,要判斷是否為nil就會比較尷尬了。所以用flatMap會更安全一點。

let a = Optional(10)
let b = a.map { $0 + 1 }
let c = a.map { value in
    return value == 10 ? nil : value
}
let d = a.flatMap { value in
    return value == 10 ? nil : value
}

print("b == \(b)")
// b == Optional(11)
print("c == \(c)")
// c == Optional(nil)
if let e = c {
    // 嵌套的Optional,即使用Optional bind也無法判斷里面是否為nil
    print("e == \(e)")
    // e == nil
}
if let f = d {
    // 這里不會打印,因為判斷到f為nil。
    print("f == \(f)")
}

Swift的數(shù)組也是一個Monad,但它有兩種實現(xiàn)flatMap的方式

public func flatMap<SegmentOfResult : Sequence>(_ transform: (Element) throws -> SegmentOfResult) rethrows -> [SegmentOfResult.Iterator.Element]
    
public func flatMap<ElementOfResult>(_ transform: (Element) throws -> ElementOfResult?) rethrows -> [ElementOfResult]

從函數(shù)原型來看,兩個函數(shù)長得很像。區(qū)別在于泛型上,一個函數(shù)限定了數(shù)組必須是嵌套的,第二個函數(shù)則沒限定。下面看看實例。

let a:[Int?] = [1, 2, nil, 4]
let b = [[1, 2], [3, 4]]

let c = a.flatMap { $0 }
let d = b.flatMap { $0 }

print(c)
// [1, 2, 4]
print(d)
// [1, 2, 3, 4]

可以看出兩個flatMap的區(qū)別了,對于第一個函數(shù),它與map最大的區(qū)別是,它會將數(shù)組里的元素進行解包并過濾掉nil。第二個函數(shù)則將二維數(shù)組打平。


寫在最后

函數(shù)式編程的三兄弟是一個比較大的主題,我的理解只是冰山一角,但帶上這些片面的理解能讓我更好的理解map, flatMap這些函數(shù)的意義,還有RAC。下一遍將討論一下RAC和Monad。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容