Edited at

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

More than 3 years have passed since last update.

こんにちは!

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

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

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

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


ScalikeOptional.swift

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

GitHubにあげました

https://github.com/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 p: 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便利ですねー