SwiftのOptionalにちょっとした関数を定義して、ちょびっとFunctionalに扱えるようにしてみます。
※ちなみにモナドとかとはまた別のお話です。
if let
を関数で
通常,nilかどうかチェックしてunwrapする場合にif let
を使うのですが、
これを関数で行えるようにしてみます。
let num: Int? = 100
if let num = num {
print("output:", num)
}
do
の定義
こんな感じで do
関数を定義します。
extension Optional {
@discardableResult
func `do`(block: (Wrapped) -> Void) -> Optional<Wrapped> {
if case .some(let wrapped) = self {
block(wrapped)
}
return self
}
}
値がある場合には、渡されたclosureに対してunwrapした値を渡します。そして、この関数の返り値として、自分自身を渡してあげます。
使ってみる
if let
の代わりとして使うこともできるし、関数の戻り値としてOptional自身をそのまま返すので、他の関数に繋ぐこともできます。
let num: Int? = 100
num.do { print("output:", $0) }
// if letで行っていた出力をしつつ、mapでStringに変換する
let numString = num.do { print("output:", $0) }.map { "\($0)" }
??
を関数で
nil-coalescing operator (??
)も関数化してみます。
let array: [Int] = []
// 配列を最初があればそれを、なければ(=nil)デフォルトとして0を代入する
let num: Int = array.first ?? 0
こういうやつですね。
関数を定義する
extension Optional {
func get(or `default`: @autoclosure () -> Wrapped) -> Wrapped {
if case .some(let wrapped) = self {
return wrapped
}
return `default`()
}
}
ポイントとしてはunwrap出来た場合はその値を、そうでなければ引数で渡した値を返却するようにしています。
また、引数に対して @autoclosure
をかけてあげると、
unwrap出来た場合に引数で渡した部分が評価されないので、若干やさしいです。
というよりも、 ??
自体が @autoclosure
かかっているので、それに合わせた方が良さそうです。
使ってみる
すごい適当な例ですが。
let username: String? = user.name
// 通常のOptional Biding
let uppercasedName = (username ?? getDefaultName()).uppercased()
// get(or:)を使ってみる。
// 2を採用した場合、unwrap出来た場合は `getDefaultName()` が評価されずに済む
let uppercasedName = username.get(or: getDefaultName()).uppercased()
let greeting = "Hello, \(uppercasedName))"
let array: [Int] = []
let num: Int = array.first.get(or: 0)
あまり大差ないですが、 ??
だと (optional ?? defaultValue) みたいに括弧で囲まないと次の関数に繋げられないので
ちょっとスッキリしますね。
まとめ
そんなに頻繁に使うものじゃないけど、普段使っているものも別の形で書けるし、ちょびっとFunctionalに扱えるようになるよ、という紹介でした。