Swift で Scala の Option#fold を実装してみる


あらすじ

関数型プログラミング勉強していたら、foldr と foldl の理解に躓きそうになっている僕。Swiftには foldl に該当する reduceがあるけど、foldr がよくわからずに理解しようと思っていたら、Scala の Option#foldに出会う。

Scala の Option#fold とは

def fold[B](ifEmpty:  B)(f: (A)  B): B

型を変換しながら、もとの値が Some か None かによって、別々の値を返す事が出来ます。

これをSwiftで実装したいわけです。Optional を extension で拡張してみましょう。


extension Optional {
    func fold<T>(_ ifEmpty: T, _ safe: (_ me: Wrapped) -> T ) -> T {
        if let wrapped = self {
            return safe(wrapped)
        } else {
            return ifEmpty
        }
    }
}

Optionalの値が nil だったら デフォルトとして 第一引数の<T> 型の値を、そうじゃなければ クロージャを実行した<T>型の戻り値を返すという関数になります。

オプショナルを if let で条件分岐するのはSwiftだと良く書くコードですが、これだとスッキリしますね!

//ベタな条件分岐を利用した処理
var some: Int?
var message: String
if let value = some {
    message = "\(value) is value"
} else {
    message = "value is nil"
}

//Optional fold を利用すると
let foldMessage = some.fold("value is nil"){ return "\($0) is value" }

使い方はこんな感じ。


struct Food {
    let calorie: UInt
    init(_ calorie: UInt) {
        self.calorie = calorie
    }
}

func eat(food: Food?) {
    let message = food.fold("ダイエット中だ!"){ return "\($0.calorie) kcal 肥えました!" }
    NSLog(message)
}

eat(food: Food(1000)) // 1000 kcal 肥えました!
eat(food: nil) // ダイエット中だ!