EZ-‐‑‒NET  熊⾕谷友宏  
http://ez-‐‑‒net.jp/
Swift 2.0
2015.06.28  
@  Swift  2  (&  LLDB)  シンポジウム
⼤大域関数の⾏行行⽅方から
熊谷友宏
EZ-NET http://ez-net.jp/
@es_kumagai
Xcode 5 徹底解説
IP Phone 音でダイヤル 音で再配達ゴッド
いつもの電卓
with 割勘ウォッチ
MOSA
Swift 2.0 で
衝撃的だった出来事
大域関数の大胆な削減
• 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
• 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
#################################################################################
思ったほどは消えてない?
++
-------------------------
なぜ大胆に削除されたと感じたか
• 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
削除された大域関数のすべてが
ジェネリック関数
おさらい
プロトコル
型の振る舞いを決めるもの
ジェネリック
型に縛られないプログラミング
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>(collection:T)
-> T.Index.Distance {
let start = collection.startIndex
let end = collection.endIndex
return distance(start, end)
}
ジェネリック関数
プロトコルを想定して機能をつくる
保証された機能を使ってプログラミング
Swift 1.2
機能ができたら型をつくる
struct Month {
var days:Array<Day>
init(days:Int) {
self.days = (1...days).map(Day.init)
}
}
型をつくる
データ構造を決める
Swift 1.2
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
機能を使う
機能を使う
ジェネリック関数を使う
let june = Month(days: 30)
// 数えられるようになっている
count(june)
Swift 1.2
struct Year : CollectionType {
:
:
}
機能を使う
新しく型を作ったときも使える
let year = Year(2015)
// 同じ関数をすぐに使える
count(year)
Swift 1.2
ジェネリックプログラミング
振る舞いに着目して組み上げるのが
ジェネリックプログラミング
得られる恩恵
▶ 型を作るときに構造に専念できる
▶ 型に備えるべき機能がプロトコルで判る
▶ 機能を共用化できる
▶ 未知の型にも対応できる
▶ 準拠するプロトコルから出来ることが判る
大域関数の削除
大域関数の削除が気になったのか
プロトコルを活かす仕組みだったから
そもそもなぜ
protocol CollectionType {
}
プロトコルと機能の実装
プロトコルを定義して
それを使った型を作る
struct Array<T> : CollectionType {


}
struct Dictionary<Key,Value> : CollectionType {

}
Swift 1.2
大域にジェネリック関数を えると
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
気になっていたこと
Swift 1.2 で
struct Array<T> : CollectionType {
var count: Int { get }
}
両方に同じ機能がある
型にも実装されていたり
Swift 1.2
大域関数に実装されているものが
func count<T:CollectionType>(x:T)

-> T.Index.Distance
なぜ両方にあるのか
▶ 大域関数にある機能をなぜ型にも?
▶ array.count と書く方が便利だから?
let c = count(array)
let c = array.count
どちらも同じ機能
Swift 1.2
大域関数だけで良いのでは…?
Swift 1.2
むしろ大域関数が消滅
Swift 2.0
大域関数の行方
削除だけでは済まないはず
それぞれの型に固有な実装へ
汎用的な大域関数が
Swift 2.0
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
移動先の多くが
Protocol Extension
Protocol Extension
▶ プロトコルを拡張する機能
▶ 既定の実装を記載できる
▶ プロトコルに規定された機能で実装する
▶ 型エイリアスの種類で実装を変えられる
Swift 2.0
特徴
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
たとえば、こんなプロトコルがあったとき
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
プロトコル拡張で振る舞いから機能を実装
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
型では最低限の機能だけを実装すれば
let month = Month()
month.count
month.indices
Protocol Extension
Swift 2.0
プロトコル拡張で実装した機能も使える
つまり
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
}
// 振る舞いをプロトコルで規定
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
嬉しいポイント
Protocol Extension
let array = Array<String>()
array.count
1. コード補完が効く
Protocol Extension
▶ 所属する機能として候補に挙がる
▶ 思考の流れどおりに書ける
extension CollectionType

where Element : IntegerType {
var total:Element {
return self.reduce(0, combine:+)
}
}
2. 条件付きで拡張できる
Protocol Extension
▶ 型エイリアスで条件を指定できる
▶ 条件を満たす型にだけ機能が追加される
let intArray = Array<Int>()
let strArray = Array<String>()
intArray.total
strArray.total
2. 条件付きで拡張できる
Protocol Extension
▶ 条件を満たす型でだけ使える
▶ それ以外の型にはそもそも存在しない
プロトコルを活かせる理想形
Protocol Extension は
Protocol Extension に
想いを馳せる日々
役割について
ジェネリック関数を置き換える機能?
Protocol Extension
〓
Protocol Extension
おさらい
▶ ジェネリック関数と同等のことができる
▶ 機能がプロトコルでグループ化される
▶ 補完機能が働くのが嬉しい
大域関数が要らなくなる?
Protocol Extension
ジェネリック関数にしかできないこと
▶ 名前空間による完全な衝突回避
衝突の可能性
▶ プロトコル自体は名前空間で回避できる
▶ 要求する機能が衝突すると回避できない
衝突について
1. プロトコル名の衝突
複数のモジュールで同じ名前なら回避可能
// JSON Module
protocol ValueType {
}
// XML Module
protocol ValueType {
}
// モジュールを明記することで回避可能
struct JSONValue : JSON.ValueType {
}
2. 機能の衝突
異なる目的を同じ定義が求めたときは衝突
protocol PNGType {
var data:NSData{get}
}
protocol JPEGType {
var data:NSData{get}
}
struct Picture : PNGType, JPEGType {
// どちらの data を期待している?
var data:NSData {
}
}
プロトコル名での区別ができない
2. 機能の衝突
機能名を具体的なものにして衝突を回避する方法
protocol PNGType {
var pngData:NSData
{get}
}
protocol JPEGType {
var jpegData:NSData
{get}
}
struct Picture : PNGType, JPEGType {
// もし名前が衝突しても同じ目的の可能性が高い
var jpegData:NSData {
}
}
名前が長くなりすぎないようには心掛けたい
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 … }
}
独自の型を使えばほぼ衝突は回避できる
struct Picture : ProtoA, ProtoB {
3. 型エイリアスの衝突
同名の型エイリアスが異なる型を期待すると衝突
protocol ProtoA {
typealias Element
:
protocol ProtoB {
typealias Element
:
でも具体例が思いつかない…!
衝突は案外、心配なさそう
積極的に活用して行くのが良さそう
Protocol Extension は
そもそもの extension について
カテゴリ拡張は避けられていた感
Objective-C の頃は
クラスに後から独自のメソッドを追加する機能
【カテゴリ拡張】
不安を感じない気がする
Swift だと extension を使うことに
▶ Objective-C はクラス継承が主体で

影響範囲が広すぎたから…?
▶ プロトコルの継承が主体で

拡張するにも動機が明確だから…?
extension の使いどころについて
Protocol Extension
使いどころの判断基準
▶ 大域関数の代わりとして積極的に活用?
▶ 演算子の実装を除くすべて?
▶ 既存のプロトコルも積極的に拡張?
普通の書き方として使っていくべき?
Any や AnyObject は拡張できない
任意の型やオブジェクトに対する
機能の実装には大域関数が必要
Protocol Extension
拡張できないプロトコルも存在する
( Non-nominal type cannot be extended )
Protocol Extension
Swift 2.0 の標準機能は大域関数も存在する
▶ なぜ大域関数として残されているのか
▶ Protocol Extension だけでは問題なのか?
func abs<T:SignedNumberType>(x:T) -> T
func advance<T:ForwardIndexType>
(start:T, _ n:T.Distance) -> T
なにか理由があるのだろうか
Protocol Extension
存在理由が分かる気がする大域関数
▶ stride は気持ちが分かる気がする
▶ Strideable を操作するのではなく

値を使って Stride 型を得ることが目的
▶ 実際 Protocol Extension されていない
func stride<T:Strideable>(from start:T,
through end:T, by stride:T.Stride) ->
StrideThrough<T>
Protocol Extension
存在理由がよくわからない大域関数
▶ Protocol Extension だけで十分そう
▶ 深い事情で残されているわけではない?
▶ それとも inout が何か を握っている?
func removeAll
<C:RangeReplaceableCollectionType>
(inout x:C, keepCapacity:Bool = default)
まとめ
Swift 2.0 大域関数の行方から
▶ 大域関数の大胆な削減
✴ 大域関数は Protocol Extension へ
▶ Protocol Extension の気になるところ
✴ 名前の衝突は心配要らなそう?
✴ 既存プロトコルも積極的に拡張できそう?
▶ Protocol Extension の使いどころ
✴ 大域関数を置き換える機能?
✴ 大域関数は標準ライブラリに残っている
✴ 使い分けのポイントがあるのだろうか

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