(discord/swift/2018/03/15/0) tryとthrowをwrapしたrecover拡張について
概要
- discord ios dev
- #swift
- 2018/03/15
- tryとthrowをwrapしたrecover拡張について
log
omochimetaru
- 2018/03/15
@lovee こんなのできた
public func recover<R>(_ f: () throws -> R, ifError: (Error) -> R) -> R { do { return try f() } catch let error { return ifError(error) } } public func recover<R>(_ f: () throws -> R, ifError: (Error) throws -> R) throws -> R { do { return try f() } catch let error { return try ifError(error) } }
この前言っていたguard + tryみたいなパターンは、R をOptional にして使えば
guard let x = recover (...) else { return }
の形で書けるし、
2つめのオーバーロードを使えば、リカバリできないエラー型の場合は
そのまま更に上まで飛んで行く。
使用例
guard let modelScene = recover({ () -> SCNScene? in try SCNScene(url: url) }, ifError: { error in self.handleError(error) return nil }) else { return }
lovee
- 2018/03/15
ふむふむ、だいぶ長くなってしまうところ以外は良さげですね
omochimetaru
- 2018/03/15
うん、あとOptional推論がうまくいかないから明示しているんだよね
名前ちょっと変えてかならず Optional になる仕様のほうが使いやすいかも
lovee
- 2018/03/15
もしかすると ifError のところと最後のところの戻り値は Optional
omochimetaru
- 2018/03/15
あ〜 recover 自体を try? でたたむ手があるな
guard let modelScene = try? recover({ try SCNScene(url: url) }, ifError: { error in self.handleError(error) throw error }) else { return }
lovee
- 2018/03/15
できた
import SceneKit public func recover <R> (_ f: () throws -> R, ifError: (Error) -> Optional<R>) -> Optional<R> { do { return try f() } catch let error { return ifError(error) } } func test() { let url = URL(fileURLWithPath: "http://www.google.com") guard let _ = recover({ () -> SCNScene in try SCNScene(url: url) }, ifError: { error in print(error) return nil }) else { return } }
omochimetaru
- 2018/03/15
これだと型ヒントは消せる
lovee
- 2018/03/15
ファイル名対応してないんだDiscord
omochimetaru
- 2018/03/15
そのパターンでもクロージャの型ヒント消せそう。
guard let modelScene = recoverToOptional({ try SCNScene(url: url) }, ifError: { error in self.handleError(error) return nil }) else { return }
行数短くしたいですねえ。
guard let modelScene = recoverToNone({ try SCNScene(url: url) }, ifError: self.handleError) else { return }
lovee
- 2018/03/15
よし、ここまで書けた
import Foundation public func recover <R> (_ f: () throws -> R, ifError: (Error) -> Void) -> Optional<R> { do { return try f() } catch let error { ifError(error) return nil } } struct Test { let url: URL enum InitError: Error { case unknown } init(urlString: String) throws { guard let url = URL(string: urlString) else { throw InitError.unknown } self.url = url } } func test(_ string: String) { guard let test: Test = recover({ return try Test(urlString: string) }, ifError: { print($0) }) else { return } print(type(of: test)) } test("http://www.google.com") // Test test("") // unknown
omochimetaru
- 2018/03/15
try test のところのreturnいらなそう
僕はそのrecoverはrecoverToNoneって名前にした
lovee
- 2018/03/15
まあ1行だけならね
tarunon
- 2018/03/15
nits 第一引数に @autoclosure つけるといいかも
koher
- 2018/03/15
↑を読みながら @autoclosure が良さそうだなと思って最後まで読んだら先に言われてた:rolling_eyes:
omochimetaru
- 2018/03/15
autoclosureもありだけど個人的には何が起きてるかわかりにくくなりそうなので微妙だなあ
koher
- 2018/03/15
用途を考えると single-expression closure になることが多いだろうからまさに @autoclosure の出番だと思うけどなぁ。
omochimetaru
- 2018/03/15
まあ特に標準提案したいというほどの気持ちは無いのでお好みで
tarunon
- 2018/03/15
多分なんですがthrowsの値を使って何かするみたいなfunction書くときはそもそもそれをthrowsにして、
非throwsとの境界をそこに設定するのが良いのではと思った
func doSomething() { return try? doSomething() ?? /* 何か */ } func doSomething() throws { let x = try throwableFunc1() let y = try throwableFunc2() ... }
こんなイメージ
omochimetaru
- 2018/03/15
↑それだとtry?でnilになるから「throwsの値」(飛んでくるエラーオブジェクトの事だよね)が使えない
tarunon
- 2018/03/15
あーー ハマり たしかにそうだ
omochimetaru
- 2018/03/15
エラーを使いながらもthrow伝搬を止めて値に畳み込みたいときに、簡単に書くことができないんだよね。
do - catch だと式じゃないから式の中に埋め込めないし。