swift - result型で結果を返すのは邪道か,王道か
TRANSCRIPT
Objective-C厨がSwiftにハマったでござる
Programming
Yuichi YoshidaChief engineer, DENSO IT Laboratory, Inc.
#potatotips
@sonson_twit
© 2014 Yuichi Yoshida, All rights reserved. Redistribution or public display not permitted without written permission from Yuichi Yoshida.
Swift - Result<T>型で結果を返すのは邪道か,王道か
自己紹介2tchの中の人
• iOS好きです • 2tch(2ちゃんねるビューア) • iOS SDK Hacksなど
• 研究・開発 • コンピュータビジョン • 機械学習 • 画像検索サービスとか • 車向けサービスやハードウェアとか
reddiftSwift Reddit API Wrapper
• 1億人以上のアメリカのSNS • APIあり • Objective-CのAPI Wrapperはあり
• OAuth2に対応してない • Swiftじゃない
• よし,いっちょ,勉強がてら作るか! • MIT License
https://github.com/sonsongithub/reddift
よくあるコード?
func linkList( paginator:Paginator?, sortingType:ListingSortType, subreddit:Subreddit?, completion:( links:[Link], paginator:Paginator?, error:NSError? )->Void) -> NSURLSessionDataTask
ダウンロード後のコールバックをブロックで渡す
書き方もありますが func linkList(paginator:Paginator?, sortingType:ListingSortType, subreddit:Subreddit?, completion:(links:[Link], paginator:Paginator?, error:NSError?)->Void) -> NSURLSessionDataTask { var parameter:[String:String] = [:] if let paginator = paginator { if paginator.sortingType == sortingType { parameter = paginator.parameters() } } var path = sortingType.path() if let subreddit = subreddit { path = "/r/\(subreddit.display_name)\(path)" } var URLRequest = NSMutableURLRequest.mutableOAuthRequestWithBaseURL(baseURL, path:path, parameter:parameter, method:"GET", token:token) let task = URLSession.dataTaskWithRequest(URLRequest, completionHandler: { (data:NSData!, response:NSURLResponse!, error:NSError!) -> Void in self.updateRateLimitWithURLResponse(response) if error != nil { dispatch_async(dispatch_get_main_queue(), { () -> Void in completion(links:[], paginator: nil, error: error) }) } else { if let json:[String:AnyObject] = NSJSONSerialization.JSONObjectWithData(data, options:NSJSONReadingOptions.allZeros, error: nil) as? [String:AnyObject] { let (links, paginator) = self.parseLinkListJSON(json) if links.count > 0 && paginator != nil { paginator?.sortingType = sortingType; dispatch_async(dispatch_get_main_queue(), { () -> Void in completion(links:links, paginator:paginator, error:nil) }) } else { dispatch_async(dispatch_get_main_queue(), { () -> Void in completion(links:links, paginator:paginator, error:NSError.errorWithCode(0, userinfo: ["error":"Can not get any contents expectedly."])) }) } } else { dispatch_async(dispatch_get_main_queue(), { () -> Void in completion(links:[], paginator: nil, error:NSError.errorWithCode(0, userinfo: ["error":"Can not parse response object."])) }) } } }) task.resume() return task }
エラーと戻り値がぐちゃぐちゃに
Functional Concepts and Generics
session?.getList(. . . . . . , completion: { (result) in switch result { case let .Error(error): println(error.code) case let .Value(box): println(box.value) // do something } })
https://robots.thoughtbot.com/efficient-json-in-swift-with-functional-concepts-and-generics
基本,ここと岸川さんの受け売り
戻り値をResult<T>にする
public enum Result<A> { case Success(Box<A>) case Failure(NSError) public init(value:A) { self = .Success(Box(value)) } public init(error: NSError) { self = .Failure(error) } ........ }
public final class Box<A> { public let value: A public init(_ value: A) { self.value = value } }
正しい戻り値か,Errorオブジェクトのどちらかを返す
enumになる
戻り値へのアクセスのイメージ
(a : Result<Hoge>) -> Void { switch(a) {
case let .Success(x): println(x.value)
case let .Failuare(x): println(x.error)
} }
んで演算子定義と組み合わせると,
let task = URLSession.dataTaskWithRequest(request, completionHandler: { (data:NSData!, response:NSURLResponse!, error:NSError!) -> Void in let responseResult = resultFromOptionalError(Response(data: data, urlResponse: response), error)
let result = responseResult >>> parseResponse >>> decodeJSON
completion(result) }) task.resume()
もはや後から読めない綺麗なコードができあがる!!!
まとめ,ではなく議論• Result<A>で書くと綺麗
• 演算子定義もやるとさらに綺麗 • 他の書き方もある
• flatMap? • 多くのOSSでみんな車輪の再発明をやってる
• 読みやすいか • 書きやすいか • デバッグしやすいか
議論したいです,ハイ