Last active
August 26, 2020 10:04
-
-
Save tarunon/2e37aa4e4c702f65e49a322c80dee13f to your computer and use it in GitHub Desktop.
>>=.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// tryがResult.flatMapのdo記法であることの確認。 | |
// 逆になるが、Result.flatMapから考えていったほうが理解しやすいのでその順番で。 | |
func foo() -> Result<Int, Error> { | |
.success(1) | |
} | |
func bar(_ arg: Int) -> Result<Int, Error> { | |
.success(arg + 1) | |
} | |
// 他の関数も存在すると仮定する | |
let hoge = bar | |
let fuga = bar | |
let piyo = bar | |
// fooとbarの関数を合成することを考える。 | |
// fooが成功した場合、その成功した値をbarの引数に渡したい。 | |
// ResultはモナドなのでflatMapを使う。 | |
func composeUsingFlatMap() -> Result<Int, Error> { | |
foo().flatMap { bar($0) } | |
} | |
// 2つだと難しくは無いが、例えば合成したい関数が増えるとflatMapを大量に呼ばなければならなくなる、 | |
// またflatMapをコールする以上、変数を挟むことがMustとなる、という不満が出てくる | |
// これは大量のflatMapを呼んでいる例。 | |
func tooManyFlatMap() -> Result<Int, Error> { | |
return foo() | |
.flatMap { bar($0) } | |
.flatMap { hoge($0) } | |
.flatMap { fuga($0) } | |
.flatMap { piyo($0) } | |
} | |
// もしResultでなくIntを返す関数であるなら、1行で関数合成することも可能だ。 | |
func composeCommonFunctions() -> Int { | |
func foo() -> Int { 1 } | |
func bar(_ arg: Int) -> Int { arg + 1 } | |
let hoge = bar | |
let fuga = bar | |
let piyo = bar | |
return piyo(fuga(hoge(bar(foo())))) | |
} | |
// そこで出てくるのがdo記法。SwiftだとResult.flatMapに対してはtryが相当する。 | |
// .get()でthrowsに変換出来るので、それを利用する。 | |
// 大量のflatMapが必要無くなったことが解る。 | |
func composeUsingTryAndTmp() throws -> Int { | |
let a = try foo().get() | |
let b = try bar(a).get() | |
let c = try hoge(b).get() | |
let d = try fuga(c).get() | |
return try piyo(d).get() | |
} | |
// もちろん変数を経由せずに一行にしても良い。 | |
func composeUsingTry() throws -> Int { | |
try piyo(try fuga(try hoge(try bar(try foo().get()).get()).get()).get()).get() | |
} | |
// 1行に1つのtryも可能だが、これは上記の省略になる。 | |
func composeUsing1Try() throws -> Int { | |
try piyo(fuga(hoge(bar(foo().get()).get()).get()).get()).get() | |
} | |
// ここまでがtryがResult.flatMapのdo記法であることの解説。 | |
// これを踏まえた上で以下を考える。 | |
func composeUsing1TryOptional() -> Int? { | |
try? piyo(fuga(hoge(bar(foo().get()).get()).get()).get()).get() | |
} | |
// もちろん関数合成した上で、結果のthrowsをnilにしてOptionalに変換してる、という考え方もアリ。 | |
// 或いは、それぞれの関数にtry?がかかっている、という考え方も出来る。その場合対応するflatMapはOptional.flatMapになり、flatMap記法で表現すると下記となる。 | |
func composeUsingTryOptionalAndFlatMap() -> Int? { | |
(try? foo().get()) | |
.flatMap { (try? bar($0).get()) } | |
.flatMap { (try? hoge($0).get()) } | |
.flatMap { (try? fuga($0).get()) } | |
.flatMap { (try? piyo($0).get()) } | |
} | |
// SwiftにはOptional.flatMapのdo記法は一応存在していて、Optional Bindingで記述出来る。 | |
// try?を各行に展開した書き方があるとすれば、これが該当するだろう。 | |
// ただしOptional Bindingでは変数を省略したワンライナーにはできない。 | |
func composeUsingTryOptionalAndOptionalBinding() -> Int? { | |
guard let a = try? foo().get(), | |
let b = try? bar(a).get(), | |
let c = try? hoge(b).get(), | |
let d = try? fuga(c).get(), | |
let e = try? piyo(d).get() else { return nil } | |
return e | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment