FLINTERS Engineer's Blog

FLINTERSのエンジニアによる技術ブログ

SwiftのOptionalをScala風に拡張するExtensionを書いてみた

こんにちは!寺坂です。

普段iOSではSwiftを、Androidやサーバー側の開発ではScalaを利用して開発をしています。

どちらも微妙に似ていて、けれど、やはり違っているので、

たまにSwiftならこう書けるのに、とかScalaならこう書けるのに、と思うことがあります。

なので、SwiftのOptionalをScalaっぽく扱えるように拡張してみました。

ScalikeOptional.swift

ワンライナー縛りで書いています。

GitHubにあげました

GitHub - iTerasaka/ScalikeOptional.swift

// MARK: - ScalikeOptional.swift

extension Optional {
   
    static func empty() -> Wrapped? {
        return Optional<Wrapped>.None
    }

    public var get: Wrapped {
        return self!
    }

    public var isEmpty: Bool {
        return self.map { _ in false } ?? true
    }
    
    public var isDefined: Bool {
        return !self.isEmpty
    }
                                                    
    public func getOrElse(p: Wrapped) -> Wrapped {
        return self ?? p
    }
                                                                                    
    public func orElse(p: Wrapped) -> Wrapped? {
        return Optional(self ?? p)
    }
                                                                                                    
    public func forEach(@noescape f: Wrapped -> Void) -> Void {
        if let some = self { f(some) }
    }
    
    public func forAll(@noescape f: Wrapped -> Bool) -> Bool {
        return self.map(f) ?? true
    }
    
    public func fold<A>(ifEmpty: A, @noescape _ f: Wrapped -> A) -> A {
        return self.map(f) ?? ifEmpty
    }
                                                                                                                                                    
    public func exists(@noescape f: Wrapped -> Bool) -> Bool {
        return self.map(f) ?? false
    }
                                                                                                                                                                    
    public func collect<A, B>(@noescape f: A -> B) -> B? {
        return (self as? A).map(f) ?? nil
    }
                                                                                                                                                                                    
    public func filter(@noescape p: Wrapped -> Bool) -> Wrapped? {
        return self.exists(p) ? self : nil
    }
}

試してみる

// MARK: - prepare

var str: String? = "hoge"
var strOpt: String? = nil

// MARK: - property

str.get // "hoge"
strOpt.get // throw Error

str.isEmpty // false
strOpt.isEmpty // true

str.isDefined // true
strOpt.isDefined // false

strOpt.getOrElse("空です") // "hoge"
strOpt.getOrElse("空です") // "空です"

strOpt.orElse("空です") // Optional("hoge")
strOpt.orElse("空です") // Optional("空です")

// MARK: - forEach

str.forEach { print("\($0)がはいっています") } // print(hogeがはいっています)
strOpt.forEach { print("\($0)がはいっています") } // なにもしない

// MARK: - forAll

str.forAll { $0 == "h" } // false
str.forAll { $0 == "hoge" } // true
strOpt.forAll { $0 == "hoge" } // true

// MARK: - exists

str.exists { $0 == "h" } // false
str.exists { $0 == "hoge" } // true

// MARK: - fold

str.fold(false) { return $0 == "hoge" } // true
str.fold(false) { return $0 == "h" } // false

strOpt.fold(false) { return $0 == "hoge" } // false
strOpt.fold(false) { return $0 == "h" } // false

// MARK: - collect

str.collect { (a:String) in return a + "がはいっています" } // hogeがはいっています
strOpt.collect { (a:String) in return a + "1" } // nil
str.collect { (a:Int) in return a + 1 } // nil
strOpt.collect { (a:String) in return a + "1" } // nil

// MARK: - filter

str.filter { $0 == "hoge" } // Optional(hoge) 
str.filter { $0 == "aaa" } // nil

結論

Optional便利ですねー

元記事はこちらです。 SwiftのOptionalをScala風に拡張するExtensionを書いてみた - Qiita