Swift 2.0 大域関数の行方から #swift2symposium

9,347 views

Published on

2015/06/28 に @k_katsumi さん主催で開催された「Swift 2 シンポジウム」 (http://realm.connpass.com/event/16556/) で発表させて頂いた資料です。

初めて Swift 2.0 に触れたときに大域関数がごっそり削除されていた衝撃から、それを敢行する上での柱となっていた Protocol Extension の魅力を紹介し、それを以って積極的に使っても良いものなのかを議論させて頂きました。

後半は疑問系が連発しますが「自分はそう思ってるけれど、どう感じますか?」という意味合いです。自分はこう思う!とかありましたらぜひぜひ教えてくださいませ。

Published in: Technology

Swift 2.0 大域関数の行方から #swift2symposium

  1. 1. EZ-‐‑‒NET  熊⾕谷友宏   http://ez-‐‑‒net.jp/ Swift 2.0 2015.06.28   @  Swift  2  (&  LLDB)  シンポジウム ⼤大域関数の⾏行行⽅方から
  2. 2. 熊谷友宏 EZ-NET http://ez-net.jp/ @es_kumagai Xcode 5 徹底解説 IP Phone 音でダイヤル 音で再配達ゴッド いつもの電卓 with 割勘ウォッチ MOSA
  3. 3. Swift 2.0 で 衝撃的だった出来事
  4. 4. 大域関数の大胆な削減
  5. 5. • abs • advance • alignof • alignofValue • assert • assertionFailure • contains • count • debugPrint • debugPrintln • distance • dropFirst • dropLast • dump • enumerate • equal • extend • fatalError • filter • find • first • flatMap • getVaList • indices • insert • isEmpty • isUniquelyRefer enced • isUniquelyRefer encedNonObjC • join • last • lazy • lexicographicalC ompare • map • max • maxElement • min • minElement • numericCast • overlaps • partition • precondition • preconditionFail ure • prefix • print • println • reduce • reflect • removeAll • removeAtIndex • removeLast • removeRange • reverse • sizeof • sizeofValue • sort • sorted • splice • split • startsWith • stride • strideof • strideofValue • suffix • swap • toDebugString • toString • transcode • underestimateC ount • unsafeAddress Of • unsafeBitCast • unsafeDowncast • unsafeUnwrap • withExtendedLif etime • withUnsafeMuta blePointer • withUnsafeMuta blePointers • withUnsafePoint er • withUnsafePoint ers • withVaList • zip Swift 1.2
  6. 6. • abs • advance • alignof • alignofValue • assert • assertionFailure • contains • count • debugPrint • debugPrintln • distance • dropFirst • dropLast • dump • enumerate • equal • extend • fatalError • filter • find • first • flatMap • getVaList • indices • insert • isEmpty • isUniquelyRefer enced • isUniquelyRefer encedNonObjC • join • last • lazy • lexicographicalC ompare • map • max • maxElement • min • minElement • numericCast • overlaps • partition • precondition • preconditionFail ure • prefix • print • println • reduce • reflect • removeAll • removeAtIndex • removeLast • removeRange • reverse • sizeof • sizeofValue • sort • sorted • splice • split • startsWith • stride • strideof • strideofValue • suffix • swap • toDebugString • toString • transcode • underestimateC ount • unsafeAddress Of • unsafeBitCast • unsafeDowncast • unsafeUnwrap • withExtendedLif etime • withUnsafeMuta blePointer • withUnsafeMuta blePointers • withUnsafePoint er • withUnsafePoint ers • withVaList • zip • anyGenerator • readLine Swift 2.0
  7. 7. ################################################################################# 思ったほどは消えてない? ++ -------------------------
  8. 8. なぜ大胆に削除されたと感じたか
  9. 9. • contains • count • debugPrintln • enumerate • equal • filter • find • first • flatMap • indices • isEmpty • last • lexicographicalCompare • map • maxElement • minElement • partition • println • reduce • reverse • sorted • startsWith • toDebugString • toString • underestimateCount 削除された大域関数 Swift 2.0
  10. 10. 削除された大域関数のすべてが ジェネリック関数
  11. 11. おさらい
  12. 12. プロトコル 型の振る舞いを決めるもの
  13. 13. ジェネリック 型に縛られないプログラミング
  14. 14. protocol CollectionType { typealias Element typealias Index : ForwardIndexType subscript(index:Index) -> Element { get } var startIndex:Index { get } var endIndex:Index { get } } プロトコル プロトコルを定義する これらの機能が使えることを保証 Swift 1.2
  15. 15. func count<T:CollectionType>(collection:T) -> T.Index.Distance { let start = collection.startIndex let end = collection.endIndex return distance(start, end) } ジェネリック関数 プロトコルを想定して機能をつくる 保証された機能を使ってプログラミング Swift 1.2
  16. 16. 機能ができたら型をつくる
  17. 17. struct Month { var days:Array<Day> init(days:Int) { self.days = (1...days).map(Day.init) } } 型をつくる データ構造を決める Swift 1.2
  18. 18. extension Month : CollectionType { subscript(index:Int) -> Day { return self.days[index - 1] } var startIndex:Int { return 1 } var endIndex:Int { return self.days.count + 1 } } 型をつくる プロトコルに基づき振る舞いを決める Swift 1.2
  19. 19. 機能を使う
  20. 20. 機能を使う ジェネリック関数を使う let june = Month(days: 30) // 数えられるようになっている count(june) Swift 1.2
  21. 21. struct Year : CollectionType { : : } 機能を使う 新しく型を作ったときも使える let year = Year(2015) // 同じ関数をすぐに使える count(year) Swift 1.2
  22. 22. ジェネリックプログラミング 振る舞いに着目して組み上げるのが
  23. 23. ジェネリックプログラミング 得られる恩恵 ▶ 型を作るときに構造に専念できる ▶ 型に備えるべき機能がプロトコルで判る ▶ 機能を共用化できる ▶ 未知の型にも対応できる ▶ 準拠するプロトコルから出来ることが判る
  24. 24. 大域関数の削除
  25. 25. 大域関数の削除が気になったのか プロトコルを活かす仕組みだったから そもそもなぜ
  26. 26. protocol CollectionType { } プロトコルと機能の実装 プロトコルを定義して それを使った型を作る struct Array<T> : CollectionType { 
 } struct Dictionary<Key,Value> : CollectionType {
 } Swift 1.2
  27. 27. 大域にジェネリック関数を えると func count<T:CollectionType>(x:T)
 -> T.Index.Distance func indices<C:CollectionType>(x:C)
 -> Range<C.Index> let array = Array<Int>() let dictionary = Dictionary<String,Int>() count(array) count(dictionary) プロトコルと機能の実装 どちらの型にでも使える Swift 1.2
  28. 28. 気になっていたこと Swift 1.2 で
  29. 29. struct Array<T> : CollectionType { var count: Int { get } } 両方に同じ機能がある 型にも実装されていたり Swift 1.2 大域関数に実装されているものが func count<T:CollectionType>(x:T)
 -> T.Index.Distance
  30. 30. なぜ両方にあるのか ▶ 大域関数にある機能をなぜ型にも? ▶ array.count と書く方が便利だから? let c = count(array) let c = array.count どちらも同じ機能 Swift 1.2
  31. 31. 大域関数だけで良いのでは…? Swift 1.2
  32. 32. むしろ大域関数が消滅 Swift 2.0
  33. 33. 大域関数の行方
  34. 34. 削除だけでは済まないはず それぞれの型に固有な実装へ 汎用的な大域関数が Swift 2.0
  35. 35. SequenceType • contains • enumerate • filter • flatMap • lexicographicalCompare • map • maxElement • minElement • reduce • reverse • sorted • startsWith • underestimateCount MutableCollectionType • partition CollectionType • count • first • filter • isEmpty • last • indexOf (find) • indices String • init: (toString) • init:reflecting: (toDebugString) 削除 • debugPrintln • println 大域関数の行方 Swift 2.0
  36. 36. 移動先の多くが Protocol Extension
  37. 37. Protocol Extension ▶ プロトコルを拡張する機能 ▶ 既定の実装を記載できる ▶ プロトコルに規定された機能で実装する ▶ 型エイリアスの種類で実装を変えられる Swift 2.0 特徴
  38. 38. protocol CollectionType { typealias Element typealias Index : ForwardIndexType subscript(index:Index) -> Element { get } var startIndex:Index { get } var endIndex:Index { get } } Protocol Extension Swift 2.0 たとえば、こんなプロトコルがあったとき
  39. 39. extension CollectionType { var count:Index.Distance { return distance(self.startIndex, self.endIndex) } var indices:Range<Index> { return self.startIndex ..< self.endIndex } } Protocol Extension Swift 2.0 プロトコル拡張で振る舞いから機能を実装
  40. 40. struct Month : CollectionType { subscript(index:Int) -> Day { return self.days[index - 1] } var startIndex:Int { return 1 } var endIndex:Int { return self.days.count + 1 } } Protocol Extension Swift 2.0 型では最低限の機能だけを実装すれば
  41. 41. let month = Month() month.count month.indices Protocol Extension Swift 2.0 プロトコル拡張で実装した機能も使える
  42. 42. つまり
  43. 43. protocol CollectionType { typealias Element typealias Index : ForwardIndexType subscript(index:Index) -> Element { get } var startIndex:Index { get } var endIndex:Index { get } } Swift 1.2 // 機能は大域関数で提供 func count<T:CollectionType>(x:T) -> T.Index.Distance { return distance(x.startIndex, x.endIndex) } func indices<C:CollectionType>(x:C) -> Range<C.Index> { return x.startIndex ..< x.endIndex } // 振る舞いをプロトコルで規定
  44. 44. protocol CollectionType { typealias Element typealias Index : ForwardIndexType subscript(index:Index) -> Element { get } var startIndex:Index { get } var endIndex:Index { get } } extension CollectionType { var count:Index.Distance { return distance(self.startIndex, self.endIndex) } var indices:Range<Index> { return self.startIndex ..< self.endIndex } } // 振る舞いも機能もプロトコル内に集約 Swift 2.0
  45. 45. 嬉しいポイント Protocol Extension
  46. 46. let array = Array<String>() array.count 1. コード補完が効く Protocol Extension ▶ 所属する機能として候補に挙がる ▶ 思考の流れどおりに書ける
  47. 47. extension CollectionType
 where Element : IntegerType { var total:Element { return self.reduce(0, combine:+) } } 2. 条件付きで拡張できる Protocol Extension ▶ 型エイリアスで条件を指定できる ▶ 条件を満たす型にだけ機能が追加される
  48. 48. let intArray = Array<Int>() let strArray = Array<String>() intArray.total strArray.total 2. 条件付きで拡張できる Protocol Extension ▶ 条件を満たす型でだけ使える ▶ それ以外の型にはそもそも存在しない
  49. 49. プロトコルを活かせる理想形 Protocol Extension は
  50. 50. Protocol Extension に 想いを馳せる日々
  51. 51. 役割について
  52. 52. ジェネリック関数を置き換える機能? Protocol Extension 〓
  53. 53. Protocol Extension おさらい ▶ ジェネリック関数と同等のことができる ▶ 機能がプロトコルでグループ化される ▶ 補完機能が働くのが嬉しい 大域関数が要らなくなる?
  54. 54. Protocol Extension ジェネリック関数にしかできないこと ▶ 名前空間による完全な衝突回避 衝突の可能性 ▶ プロトコル自体は名前空間で回避できる ▶ 要求する機能が衝突すると回避できない
  55. 55. 衝突について
  56. 56. 1. プロトコル名の衝突 複数のモジュールで同じ名前なら回避可能 // JSON Module protocol ValueType { } // XML Module protocol ValueType { } // モジュールを明記することで回避可能 struct JSONValue : JSON.ValueType { }
  57. 57. 2. 機能の衝突 異なる目的を同じ定義が求めたときは衝突 protocol PNGType { var data:NSData{get} } protocol JPEGType { var data:NSData{get} } struct Picture : PNGType, JPEGType { // どちらの data を期待している? var data:NSData { } } プロトコル名での区別ができない
  58. 58. 2. 機能の衝突 機能名を具体的なものにして衝突を回避する方法 protocol PNGType { var pngData:NSData {get} } protocol JPEGType { var jpegData:NSData {get} } struct Picture : PNGType, JPEGType { // もし名前が衝突しても同じ目的の可能性が高い var jpegData:NSData { } } 名前が長くなりすぎないようには心掛けたい
  59. 59. 2. 機能の衝突 オーバーロードで衝突を回避する方法 protocol PNGType { var data:PNGData {get} } protocol JPEGType { var data:JPEGData {get} } struct Picture : PNGType, JPEGType { // 戻り値が違えば別物として存在できる var data:PNGData { return … } var data:JPEGData { return … } } 独自の型を使えばほぼ衝突は回避できる
  60. 60. struct Picture : ProtoA, ProtoB { 3. 型エイリアスの衝突 同名の型エイリアスが異なる型を期待すると衝突 protocol ProtoA { typealias Element : protocol ProtoB { typealias Element : でも具体例が思いつかない…!
  61. 61. 衝突は案外、心配なさそう
  62. 62. 積極的に活用して行くのが良さそう Protocol Extension は
  63. 63. そもそもの extension について
  64. 64. カテゴリ拡張は避けられていた感 Objective-C の頃は クラスに後から独自のメソッドを追加する機能 【カテゴリ拡張】
  65. 65. 不安を感じない気がする Swift だと extension を使うことに ▶ Objective-C はクラス継承が主体で
 影響範囲が広すぎたから…? ▶ プロトコルの継承が主体で
 拡張するにも動機が明確だから…?
  66. 66. extension の使いどころについて
  67. 67. Protocol Extension 使いどころの判断基準 ▶ 大域関数の代わりとして積極的に活用? ▶ 演算子の実装を除くすべて? ▶ 既存のプロトコルも積極的に拡張? 普通の書き方として使っていくべき?
  68. 68. Any や AnyObject は拡張できない 任意の型やオブジェクトに対する 機能の実装には大域関数が必要 Protocol Extension 拡張できないプロトコルも存在する ( Non-nominal type cannot be extended )
  69. 69. Protocol Extension Swift 2.0 の標準機能は大域関数も存在する ▶ なぜ大域関数として残されているのか ▶ Protocol Extension だけでは問題なのか? func abs<T:SignedNumberType>(x:T) -> T func advance<T:ForwardIndexType> (start:T, _ n:T.Distance) -> T なにか理由があるのだろうか
  70. 70. Protocol Extension 存在理由が分かる気がする大域関数 ▶ stride は気持ちが分かる気がする ▶ Strideable を操作するのではなく
 値を使って Stride 型を得ることが目的 ▶ 実際 Protocol Extension されていない func stride<T:Strideable>(from start:T, through end:T, by stride:T.Stride) -> StrideThrough<T>
  71. 71. Protocol Extension 存在理由がよくわからない大域関数 ▶ Protocol Extension だけで十分そう ▶ 深い事情で残されているわけではない? ▶ それとも inout が何か を握っている? func removeAll <C:RangeReplaceableCollectionType> (inout x:C, keepCapacity:Bool = default)
  72. 72. まとめ
  73. 73. Swift 2.0 大域関数の行方から ▶ 大域関数の大胆な削減 ✴ 大域関数は Protocol Extension へ ▶ Protocol Extension の気になるところ ✴ 名前の衝突は心配要らなそう? ✴ 既存プロトコルも積極的に拡張できそう? ▶ Protocol Extension の使いどころ ✴ 大域関数を置き換える機能? ✴ 大域関数は標準ライブラリに残っている ✴ 使い分けのポイントがあるのだろうか

×