SlideShare a Scribd company logo
My Sweet Swift
Agenda 
• Generics 
• Enumeration 
• Optional 
• Appendix
Agenda 
• Generics 
• Enumeration 
• Optional 
• Appendix 
}準備 
本題
Generics
Generics 
• 型をパラメータにとる関数・データ構造 
• 型情報を保ったまま、複数の型に同時に対応 
できる 
• とは?
例: 入力をそのまま返す関数 
func identity(input: Int) -> Int { 
return input 
} 
! 
let number = identity(42) // Int 
let string = identity("Lorem ipusm") // error
例: 入力をそのまま返す関数 
func identity(input: Any) -> Any { 
return input 
} 
! 
let number = identity(42) // Any 
let string = identity("Lorem ipsum") // Any 
! 
number * 2 // error 
string + string // error
例: 入力をそのまま返す関数 
func identity<T>(input: T) -> T { 
return input 
} 
! 
let number = identity(42) // Int 
let string = identity("Lorem ipsum") // String 
! 
number * 2 
string + string
例: map 
x 
f 
"(x)" 
func f(x: Int) -> String { 
return "(x)" 
}
例: map 
x [1, 2, 3] 
f 
"(x)"
例: map 
x [1, 2, 3] 
f 
"(x)" ["1", "2", "3"]
例: map 
x [1, 2, 3] 
f 
"(x)" ["1", "2", "3"] 
map([Int], Int -> String) -> [String]
例: map 
func f(x: Int) -> String { 
return "(x)" 
} 
! 
func map(xs: [Int], f: Int -> String) -> [String] { 
var ys = [String]() 
for x in xs { 
ys.append(f(x)) 
} 
return ys 
} 
! 
map([1, 2, 3], f) // ["1", "2", “3"] 
map([1, 2, 3], { "($0)" }) // ["1", "2", "3"]
例: map 
Int [Int] 
f 
String [String] 
map([Int], Int -> String) -> [String]
例: map 
T [T] 
T -> U 
U [U] 
map([T], T -> U) -> [U]
例: map 
func map<T, U>(xs: [T], f: T -> U) -> [U] { 
var ys = [U]() 
for x in xs { 
ys.append(f(x)) 
} 
return ys 
} 
! 
// [Int] -> [String] 
map([1, 2, 3], { "($0)" }) // ["1", "2", "3"] 
// [Int] -> [Double] 
map([1, 2, 3], { $0 * 2.0 }) // [2.0, 4.0, 6.0] 
// [Double] -> [CGRect] 
map([1, 2, 3], { CGRect(x: $0, y: $0, width: 100.0, height: 100.0) })
クラスの例: Stack 
class Stack<T> { 
private var array = [T]() 
func push(item: T) { 
array.append(item) 
} 
func pop() -> T { 
return array.removeLast() 
} 
} 
! 
let stack = Stack<Int>() 
stack.push(1) 
stack.push(2) 
stack.push(3) 
stack.pop() // 3 
stack.pop() // 2 
stack.pop() // 1
Enumerations
Enums 
enum BloodType { 
case A, B, O, AB 
} 
! 
struct Person { 
var bloodType: BloodType 
} 
! 
var p = Person(bloodType: BloodType.A) 
var q = Person(bloodType: .A) 
q.bloodType = .AB
Enums 
• ObjCのEnumと違って、「値」を持たない 
• ex. 東西南北に整数を割り当てる意味? 
• 従来通りに値を持たせることもできる 
• 文字列もOK
…with associated values 
/// ダイアログのUI要素を指定するenum。 
enum DialogItem { 
case TextButton(title: String, identifier: String) 
case GrayButton(title: String, identifier: String) 
case Text(String) 
case Title(String) 
case Margin(CGFloat) 
case Group([DialogItem]) 
} 
! 
let item1 = DialogItem.Margin(20) 
let item2 = DialogItem.Text("String")
Associated Values 
• enumの各状態に値を紐づけることができる 
• 型が違っていてもいい 
• 単なる列挙というよりは、「複数の型のどれ 
か」を表す型になる
Switchで値を展開 
switch item { 
case let .Title(title): 
println("Title: (title)") 
case let .Text(text): 
println("Text: (text)") 
case let .TextButton(title: title, identifier: identifier): 
/// 
case let .GrayButton(title: title, identifier: identifier): 
/// 
case let .Group(subitems): 
/// 
default: break 
}
例: DialogViewController 
let items: [DialogViewController.DialogItem] = [ 
.Title("ダイアログ"), 
.Text("表示要素を配列で渡すだけで、自在にダイアログを作ることができます。"), 
.TextButton(title: "ボタン", identifier: "button1"), 
.Margin(60), 
.Group([ 
.Group([.Text("二段組み"), .Text("にも")]), 
.Group([.Text("対応"), .Text("(たぶん)")]) 
]), 
.Group([ 
.TextButton(title: "ボタン", identifier: "button2"), 
.GrayButton(title: "ボタン", identifier: "button3"), 
.TextButton(title: "ボタン", identifier: "button4") 
]) 
] 
let dialog = DialogViewController(items: items, callback: nil) 
presentViewController(dialog, animated: true, completion: nil)
簡単な例: Failable 
• 「失敗するかもしれない」結果を記述する 
• 失敗すればFailed、成功すればSucceeded 
• 成功した場合は結果をAssociated Valueに持 
つ
例: Failable<T> 
enum Failable<T> { 
case Succeeded(T) // 成功したときの値 
case Failed // 失敗フラグ 
init(_ value: T) { 
self = .Succeeded(value) 
} 
}
例: Failable<T> 
// 2で割れたら割るけど割れなかったらあきらめる(やる気のない)関数 
func failableHalve(x: Int) -> Failable<Int> { 
if x % 2 == 0 { 
return .Succeeded(x / 2) 
} else { 
return .Failed 
} 
} 
! 
switch failableHalve(12) { 
case let .Succeeded(r): 
println("Answer: (r)") 
default: 
println("Odd Number") 
}
それって 
// 2で割れたら割るけど割れなかったらあきらめる(やる気のない)関数 
func failableHalve(x: Int) -> Optional<Int> { 
if x % 2 == 0 { 
return .Some(x / 2) 
} else { 
return .None 
} 
} 
! 
switch failableHalve(12) { 
case let .Some(r): 
println("Answer: (r)") 
default: 
println("Odd Number") 
}
Optionalなのでは? 
// 2で割れたら割るけど割れなかったらあきらめる(やる気のない)関数 
func failableHalve(x: Int) -> Int? { 
if x % 2 == 0 { 
return x / 2 
} else { 
return nil 
} 
} 
!! 
if let r = failableHalve(12) { 
println("Answer: (r)") 
} else { 
println("Odd Number") 
}
Optional
太古よりnullは人々を苦しめてきた
Optional以前 
• オブジェクトとはポインタである 
• 特別なアドレス(多くの場合0)をNULLと呼 
び、そこを指すポインタを「無効なオブジェ 
クト」として扱う
問題点 
• NULLを無効な値だと知っているのは実行中のプ 
ログラムとプログラマだけ 
• コンパイラには他のポインタ値と区別できない 
• →実行時に落ちる 
• 予期せぬNULLが紛れ込まないように「気をつけ 
る」「確認する」ことしかできなかった
Optionalのいいところ 
• 「無効な値」という概念を型にしたことで、 
意味論ではなく構文論として扱える 
• 無効な値を扱う操作が「書けない」文法
Optional<T> 
enum Optional<T> : Reflectable, NilLiteralConvertible { 
case None 
case Some(T) 
! 
/// Construct a `nil` instance. 
init() 
! 
/// Construct a non- `nil` instance that stores `some`. 
init(_ some: T) 
! 
/// If `self == nil`, returns `nil`. Otherwise, returns `f(self!)`. 
func map<U>(f: (T) -> U) -> U? 
! 
/// Returns a mirror that reflects `self`. 
func getMirror() -> MirrorType 
! 
/// Create an instance initialized with `nil`. 
init(nilLiteral: ()) 
}
Optional<T> 
• 実体はGeneric Enum 
• 実はちょっと嘘で、特別扱いを受けている 
• cf. http://qiita.com/koher/items/ 
5dd6ce7427e7ecff4249 
• (Failable<T>と同じように)箱です
Optional as 箱 
• 「Tかもしれない」ではなく、「Tの入る箱」 
• 値に触るためには箱を開ける必要がある 
• binding/coalescing/unwrapping 
• 箱を開けずに操作する方法もある 
• mapping/chaining
Optional Binding 
var optionalInt: Int? 
! 
if let int = optionalInt { // int: Int 
println("optionalInt was (int)") 
} else { 
println("optionalInt was nil") 
} 
! 
var array = [1, 2, 3, 4] 
while let item = array.last { // item: Int 
println("(item)") 
array.removeLast() 
}
箱を開ける: Optional Binding 
• 値があればtrueに評価されつつ中身を変数に 
束縛(bind)してくれる 
• 一度に一個しかbindできないのは多少不便
Optional Coalescing 
optionalInt ?? 3 
! 
// ↓と等価: 
optionalInt != nil ? optionalInt! : 3 
! 
// あるいは: 
func coalesce<T>(lhs: T?, rhs: @autoclosure () -> T) -> T { 
if let lhs = lhs { 
return lhs 
} else { 
return rhs() 
} 
} 
coalesce(optionalInt, 3)
箱を開ける: Optional Coalescing 
• 開けられたら開ける 
• 開けられなかったら開けない 
• かわりにデフォルト値を返す
無理矢理開ける: Forced Unwrapping 
optionalInt! 
• 値があればそれが返るが、なければ落ちる 
• 落ちる可能性があるので使うべきではない 
• 安全が保証できるなら構わないが……
箱から出さずに進める方法 
• Optional mapping 
• Optional chaining
Optional Mapping 
/// 3倍する関数 
func triple(x: Int) -> Int { 
return x * 3 
} 
! 
optionalInt = 2 
// 以下はすべて.Some 6 
optionalInt.map(triple) 
map(optionalInt, triple) 
optionalInt.map({ $0 * 3 })
箱を開けない: Optional Mapping 
• 箱の中から別の箱の中へ 
• 値があればそれを移して別の箱(Optional) 
を作る 
• nilならばnilを返す
Optional Mapping 
T 
U 
T -> U 
T? 
U? 
map(T?, T -> U) -> U?
Optional Chaining 
class A { 
var b: B? 
} 
! 
class B { 
var c: C? 
} 
! 
class C { 
var i: Int = 1 
} 
var a: A? = A() 
// a.b.c.iにアクセスしたい
Optional Chaining 
var a: A? 
! 
if let a = a { 
if let b = a.b { 
if let c = b.c { 
println("We finally find (c.i)!") 
} 
} 
}
Optional Chaining 
var a: A? 
! 
if let i = a?.b?.c?.i { 
println("Optional chaining makes (i)t easy.") 
}
箱を開けない: Optional Chaining 
• 失敗するかもしれない呼び出しを連鎖できる 
• 失敗すればその先には進まずnil
Optional Chaining 
a?.b?.c?.i 
A? 
aはA?型の変数
× 
Optional Chaining 
a.b 
A? 
A?型にbというメンバはない
Optional Chaining 
a?.b?.c?.i 
A? 
a?.bという形にする
Optional Chaining 
a?.b?.c?.i 
A() 
or 
nil 
aはnilかSomeのどちらか
Optional Chaining 
a?.b?.c?.i 
A() 
B? 
or 
nil 
Someならばbに触れる
Optional Chaining 
a?.b?.c?.i 
A() 
B? 
or 
nil nil 
nilならそれで終わり
Optional Chaining 
a?.b?.c?.i 
A() 
B? 
or 
nil nil 
bはB?型の変数
Optional Chaining 
a?.b?.c?.i 
A() 
B() 
C? 
or 
or 
nil nil 
nil 
同じように分岐
Optional Chaining 
a?.b?.c?.i 
A() 
B() 
C() 
7 
or 
or 
or 
nil nil 
nil 
nil 
以下同文
書き直してみる 
extension Optional { 
func chain<U>(f: T -> U?) -> U? { 
if let s = self { 
return f(s) 
} else { 
return nil 
} 
} 
} 
! 
if let i = a.chain({ $0.b }).chain({ $0.c }).chain({ $0.i }) { 
println("(i)") 
} 
! 
// (正確には↓だが説明は省略(結果は等しくなる)) 
// let k = a.chain({ $0.b.chain({ $0.c.chain({ $0.i }) }) })
なぜ書き直したの? 
• メンバ関数以外にも使えるようにしたい 
• これが今日の主役です
Failable Halve 
Int Int? 
// 2で割れたら割るけど割れなかったらあきらめる(やる気のない)関数 
func failableHalve(x: Int) -> Int? { 
if x % 2 == 0 { 
return x / 2 
} else { 
return nil 
} 
} 
! 
if let a = failableHalve(x) { 
println("Halve((x)) = (a)") 
} else { 
println("Failed") 
}
Failable Halve Again 
Int Int? 
if let a = failableHalve(x) { 
if let b = failableHalve(a) { 
println("Halve^2((x)) = (b)") 
} else { 
println("Failed") 
} 
} else { 
println("Failed") 
} 
Int Int?
Failable Halve The 3rd 
Int Int? 
if let a = failableHalve(x) { 
if let b = failableHalve(a) { 
if let c = failableHalve(b) { 
println("Halve^3((x)) = (c)") 
} else { 
println("Failed") 
} 
} else { 
println("Failed") 
} 
} else { 
println("Failed") 
} 
Int Int? Int Int?
Failable Halve Chaining 
Int Int? 
if let a = failableHalve(x).chain(failableHalve).chain(failableHalve) { 
println("Halve^3((x)) = (a)") 
} else { 
println("Failed") 
} 
Int? Int?
Failable Halve Chaining 
Int Int? 
Int? Int? 
24 
failableHalve(24) // Some 12 
failableHalve(24).chain(failableHalve) // Some 6 
failableHalve(24).chain(failableHalve).chain(failableHalve) // Some 3 
failableHalve(24).chain(failableHalve).chain(failableHalve).chain(failableHalve) // nil 
! 
36 
failableHalve(36) // Some 18 
failableHalve(36).chain(failableHalve) // Some 9 
failableHalve(36).chain(failableHalve).chain(failableHalve) // nil 
failableHalve(36).chain(failableHalve).chain(failableHalve).chain(failableHalve) // nil
Optional Chaining 
t: T 
T
Optional Chaining 
T f U? 
f(t): U?
Optional Chaining 
U g V? 
T f U? 
f(t): U?
Optional Chaining 
g 
f V? 
T U? 
f(t).chain(g): V?
Optional Chaining 
g 
V h W? 
f V? 
T U? 
f(t).chain(g): V?
Optional Chaining 
g h W? 
f V? 
T U? 
f(t).chain(g).chain(h): W?
Appendix
Array Mapping 
T [T] 
T -> U 
U [U] 
map([T], T -> U) -> [U]
Optional Mapping 
T 
U 
T -> U 
T? 
U? 
map( T?, T -> U) -> U?
Optional<T>とArray<T> 
• T型の値が入った箱 
• mapを使えば箱の中身だけを写すことができ 
る
Optional as 状況 
• ところで、Optionalは「nilかもしれない」と 
いう状況(context)を表現していた 
• Arrayはどんな状況を表現している? 
• →「値が確定していない」
Array as 状況 
Kyoto
目がさめると京都にいた 
(0, 0) 
Kyoto
歩く 
(0, 1) 
(-1,0) (1, 0) 
(0,-1) 
Kyoto
歩く 
/// 座標 
typealias Position = (Int, Int) 
! 
/// 東西南北のどこかへ歩きます 
func walk(from: Position) -> [Position] { 
let (x, y) = from 
return [(x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1)] 
} 
! 
walk((0, 0)) // [(-1, 0), (1, 0), (0, -1), (0, 1)]
さらに歩く 
Kyoto
さらに歩く 
Kyoto
Array Chaining 
T [U] 
U 
[V] 
V [W]
Array Chaining 
extension Array { 
func chain<U>(f: Element -> [U]) -> [U] { 
var ys = [U]() 
for x in self { 
ys += f(x) 
} 
return ys 
} 
} 
! 
walk((0, 0)).chain(walk).chain(walk)
もっと状況を! 
• 「値が非同期で返ってくる」も状況
20141128 iOSチーム勉強会 My Sweet Swift
BFTask 
doHeavyJobAsync1().continueWithBlock { 
(task: BFTask!) -> BFTask in 
let result = task.result as NSString 
self.resultLabel1.text = result 
self.resultLabel2.text = "処理中..." 
return self.doHeavyJobAsync2(result) 
}.continueWithBlock { 
(task: BFTask!) -> BFTask in 
let result = task.result as NSString 
self.resultLabel2.text = result 
self.resultLabel3.text = "処理中..." 
return self.doHeavyJobAsync3(result) 
}.continueWithBlock { 
(task: BFTask!) -> AnyObject! in 
let result = task.result as NSString 
self.resultLabel3.text = result 
return nil 
}
BFTask 
private func doHeavyJobAsync2(prevResult: String) -> BFTask { 
var completionSource = BFTaskCompletionSource() 
// 5秒待ちの処理 
// 実用的には、AFNetworkingのcompletionブロック等でsetResultするイメージ 
Util.delay(5, { 
completionSource.setResult(prevResult + "オワタ") 
}) 
return completionSource.task 
}
Task<T> 
class Task<T: AnyObject> { 
private let task: BFTask 
! 
init(task: BFTask) { 
self.task = task 
} 
var result: T? { 
return task.result as? T 
} 
func chain<U: AnyObject>(f: (T -> Task<U>)) -> Task<U> { 
let newTask = task.continueWithBlock { f($0.result as T).task } 
return Task<U>(task: newTask) 
} 
}
Taskを返す関数 
private func doHeavyJobAsync2(prevResult: NSString) -> Task<NSString> { 
let source = Source<NSString>() 
// 5秒待ちの処理 
// 実用的には、AFNetworkingのcompletionブロック等でsetResultするイメージ 
delay(5, { 
source.setResult(prevResult + "オワタ" as NSString) 
}) 
return source.task 
}
Task Chaining 
T U 
U 
V 
V W 
非同期 
非同期 
非同期
Task Chaining 
func heavyTask1(Void) -> Task<NSString> { 
return delayedTask(5) { 
NSLog("First Task (no params)") 
return "123" as NSString 
} 
} 
! 
func heavyTask2(string: NSString) -> Task<NSNumber> { 
return delayedTask(5) { 
NSLog("Second Task (param: (string))") 
return NSNumber(integer: (string as String).toInt() ?? 0) 
} 
} 
! 
func heavyTask3(number: NSNumber) -> Task<NSNumber> { 
return delayedTask(5) { 
NSLog("Third Task (param: (number))") 
return NSNumber(integer: number.integerValue * 2) 
} 
}
Task Chaining 
NSLog("Start") 
heavyTask1().chain(heavyTask2).chain(heavyTask3) 
NSLog("End") 
2014-11-27 21:15:24.659 MySweetSwift[18362:314049] Start 
2014-11-27 21:15:24.663 MySweetSwift[18362:314049] End 
2014-11-27 21:15:29.663 MySweetSwift[18362:314049] First Task (no params) 
2014-11-27 21:15:35.136 MySweetSwift[18362:314049] Second Task (param: 123) 
2014-11-27 21:15:40.636 MySweetSwift[18362:314049] Third Task (param: 123)
まとめ 
• Optionalはかわいい! 
• ArrayとOptionalは似ている 
• すなわち、箱であり、状況である 
• 非同期処理も同じように見ることができる 
• Optional Chainingのような書きかたができて、そ 
れはBFTaskに似ている
参考文献
以上

More Related Content

PDF
C++11概要 ライブラリ編
ODP
C++でのゲームプログラミングをしたときのお話 札幌C++勉強会 #4 〜スタートゲームプログラミング〜
PDF
C++コミュニティーの中心でC++をDISる
PDF
Ekmett勉強会発表資料
PDF
Pfi Seminar 2010 1 7
KEY
モナドがいっぱい!
PDF
これからの「言語」の話をしよう ―― 未来を生きるためのツール
PDF
すごいH 第12章モノイド
C++11概要 ライブラリ編
C++でのゲームプログラミングをしたときのお話 札幌C++勉強会 #4 〜スタートゲームプログラミング〜
C++コミュニティーの中心でC++をDISる
Ekmett勉強会発表資料
Pfi Seminar 2010 1 7
モナドがいっぱい!
これからの「言語」の話をしよう ―― 未来を生きるためのツール
すごいH 第12章モノイド

What's hot (20)

PDF
Haskell Lecture 2
KEY
ODP
Ekmett勉強会発表資料
PDF
今から始める Lens/Prism
PDF
Van laarhoven lens
PDF
関数型プログラミング入門 with OCaml
PDF
すごいHaskell楽しく学ぼう 第6章
ODP
Real World OCamlを読んでLispと協調してみた
PDF
関数プログラミング入門
PDF
Scala の関数型プログラミングを支える技術
PPTX
メタプログラミング C#
PDF
F#入門 ~関数プログラミングとは何か~
PDF
Swiftおさらい
PPTX
C# 式木 (Expression Tree) ~ LINQをより深く理解するために ~
PDF
MP in Scala
PDF
Scala with DDD
PDF
これから Haskell を書くにあたって
PDF
たのしい高階関数
KEY
Actor&stm
PDF
MP in Haskell
Haskell Lecture 2
Ekmett勉強会発表資料
今から始める Lens/Prism
Van laarhoven lens
関数型プログラミング入門 with OCaml
すごいHaskell楽しく学ぼう 第6章
Real World OCamlを読んでLispと協調してみた
関数プログラミング入門
Scala の関数型プログラミングを支える技術
メタプログラミング C#
F#入門 ~関数プログラミングとは何か~
Swiftおさらい
C# 式木 (Expression Tree) ~ LINQをより深く理解するために ~
MP in Scala
Scala with DDD
これから Haskell を書くにあたって
たのしい高階関数
Actor&stm
MP in Haskell
Ad

Similar to 20141128 iOSチーム勉強会 My Sweet Swift (20)

PDF
Implicit Explicit Scala
PPTX
PPTX
競技プログラミングのためのC++入門
KEY
How wonderful to be (statically) typed 〜型が付くってスバラシイ〜
PDF
TypeScript 1.0 オーバービュー
PDF
Pythonで始めるDropboxAPI
PDF
たのしい関数型
KEY
Clojure programming-chapter-2
PDF
boost tour 1.48.0 all
PPT
Pythonintro
PDF
Lisp Tutorial for Pythonista : Day 3
PDF
C++0x in programming competition
PDF
Python standard 2022 Spring
PDF
Scalaによる型安全なエラーハンドリング
PPTX
Boost jp9 program_options
PDF
速くなければスマフォじゃない - インターンバージョン-
PDF
モナドハンズオン前座
PDF
命令プログラミングから関数プログラミングへ
PDF
ノンプログラマーでも明日から使えるJavaScript簡単プログラム 先生:柳井 政和
PDF
LastaFluteでKotlinをはじめよう
Implicit Explicit Scala
競技プログラミングのためのC++入門
How wonderful to be (statically) typed 〜型が付くってスバラシイ〜
TypeScript 1.0 オーバービュー
Pythonで始めるDropboxAPI
たのしい関数型
Clojure programming-chapter-2
boost tour 1.48.0 all
Pythonintro
Lisp Tutorial for Pythonista : Day 3
C++0x in programming competition
Python standard 2022 Spring
Scalaによる型安全なエラーハンドリング
Boost jp9 program_options
速くなければスマフォじゃない - インターンバージョン-
モナドハンズオン前座
命令プログラミングから関数プログラミングへ
ノンプログラマーでも明日から使えるJavaScript簡単プログラム 先生:柳井 政和
LastaFluteでKotlinをはじめよう
Ad

20141128 iOSチーム勉強会 My Sweet Swift

  • 2. Agenda • Generics • Enumeration • Optional • Appendix
  • 3. Agenda • Generics • Enumeration • Optional • Appendix }準備 本題
  • 5. Generics • 型をパラメータにとる関数・データ構造 • 型情報を保ったまま、複数の型に同時に対応 できる • とは?
  • 6. 例: 入力をそのまま返す関数 func identity(input: Int) -> Int { return input } ! let number = identity(42) // Int let string = identity("Lorem ipusm") // error
  • 7. 例: 入力をそのまま返す関数 func identity(input: Any) -> Any { return input } ! let number = identity(42) // Any let string = identity("Lorem ipsum") // Any ! number * 2 // error string + string // error
  • 8. 例: 入力をそのまま返す関数 func identity<T>(input: T) -> T { return input } ! let number = identity(42) // Int let string = identity("Lorem ipsum") // String ! number * 2 string + string
  • 9. 例: map x f "(x)" func f(x: Int) -> String { return "(x)" }
  • 10. 例: map x [1, 2, 3] f "(x)"
  • 11. 例: map x [1, 2, 3] f "(x)" ["1", "2", "3"]
  • 12. 例: map x [1, 2, 3] f "(x)" ["1", "2", "3"] map([Int], Int -> String) -> [String]
  • 13. 例: map func f(x: Int) -> String { return "(x)" } ! func map(xs: [Int], f: Int -> String) -> [String] { var ys = [String]() for x in xs { ys.append(f(x)) } return ys } ! map([1, 2, 3], f) // ["1", "2", “3"] map([1, 2, 3], { "($0)" }) // ["1", "2", "3"]
  • 14. 例: map Int [Int] f String [String] map([Int], Int -> String) -> [String]
  • 15. 例: map T [T] T -> U U [U] map([T], T -> U) -> [U]
  • 16. 例: map func map<T, U>(xs: [T], f: T -> U) -> [U] { var ys = [U]() for x in xs { ys.append(f(x)) } return ys } ! // [Int] -> [String] map([1, 2, 3], { "($0)" }) // ["1", "2", "3"] // [Int] -> [Double] map([1, 2, 3], { $0 * 2.0 }) // [2.0, 4.0, 6.0] // [Double] -> [CGRect] map([1, 2, 3], { CGRect(x: $0, y: $0, width: 100.0, height: 100.0) })
  • 17. クラスの例: Stack class Stack<T> { private var array = [T]() func push(item: T) { array.append(item) } func pop() -> T { return array.removeLast() } } ! let stack = Stack<Int>() stack.push(1) stack.push(2) stack.push(3) stack.pop() // 3 stack.pop() // 2 stack.pop() // 1
  • 19. Enums enum BloodType { case A, B, O, AB } ! struct Person { var bloodType: BloodType } ! var p = Person(bloodType: BloodType.A) var q = Person(bloodType: .A) q.bloodType = .AB
  • 20. Enums • ObjCのEnumと違って、「値」を持たない • ex. 東西南北に整数を割り当てる意味? • 従来通りに値を持たせることもできる • 文字列もOK
  • 21. …with associated values /// ダイアログのUI要素を指定するenum。 enum DialogItem { case TextButton(title: String, identifier: String) case GrayButton(title: String, identifier: String) case Text(String) case Title(String) case Margin(CGFloat) case Group([DialogItem]) } ! let item1 = DialogItem.Margin(20) let item2 = DialogItem.Text("String")
  • 22. Associated Values • enumの各状態に値を紐づけることができる • 型が違っていてもいい • 単なる列挙というよりは、「複数の型のどれ か」を表す型になる
  • 23. Switchで値を展開 switch item { case let .Title(title): println("Title: (title)") case let .Text(text): println("Text: (text)") case let .TextButton(title: title, identifier: identifier): /// case let .GrayButton(title: title, identifier: identifier): /// case let .Group(subitems): /// default: break }
  • 24. 例: DialogViewController let items: [DialogViewController.DialogItem] = [ .Title("ダイアログ"), .Text("表示要素を配列で渡すだけで、自在にダイアログを作ることができます。"), .TextButton(title: "ボタン", identifier: "button1"), .Margin(60), .Group([ .Group([.Text("二段組み"), .Text("にも")]), .Group([.Text("対応"), .Text("(たぶん)")]) ]), .Group([ .TextButton(title: "ボタン", identifier: "button2"), .GrayButton(title: "ボタン", identifier: "button3"), .TextButton(title: "ボタン", identifier: "button4") ]) ] let dialog = DialogViewController(items: items, callback: nil) presentViewController(dialog, animated: true, completion: nil)
  • 25. 簡単な例: Failable • 「失敗するかもしれない」結果を記述する • 失敗すればFailed、成功すればSucceeded • 成功した場合は結果をAssociated Valueに持 つ
  • 26. 例: Failable<T> enum Failable<T> { case Succeeded(T) // 成功したときの値 case Failed // 失敗フラグ init(_ value: T) { self = .Succeeded(value) } }
  • 27. 例: Failable<T> // 2で割れたら割るけど割れなかったらあきらめる(やる気のない)関数 func failableHalve(x: Int) -> Failable<Int> { if x % 2 == 0 { return .Succeeded(x / 2) } else { return .Failed } } ! switch failableHalve(12) { case let .Succeeded(r): println("Answer: (r)") default: println("Odd Number") }
  • 28. それって // 2で割れたら割るけど割れなかったらあきらめる(やる気のない)関数 func failableHalve(x: Int) -> Optional<Int> { if x % 2 == 0 { return .Some(x / 2) } else { return .None } } ! switch failableHalve(12) { case let .Some(r): println("Answer: (r)") default: println("Odd Number") }
  • 29. Optionalなのでは? // 2で割れたら割るけど割れなかったらあきらめる(やる気のない)関数 func failableHalve(x: Int) -> Int? { if x % 2 == 0 { return x / 2 } else { return nil } } !! if let r = failableHalve(12) { println("Answer: (r)") } else { println("Odd Number") }
  • 32. Optional以前 • オブジェクトとはポインタである • 特別なアドレス(多くの場合0)をNULLと呼 び、そこを指すポインタを「無効なオブジェ クト」として扱う
  • 33. 問題点 • NULLを無効な値だと知っているのは実行中のプ ログラムとプログラマだけ • コンパイラには他のポインタ値と区別できない • →実行時に落ちる • 予期せぬNULLが紛れ込まないように「気をつけ る」「確認する」ことしかできなかった
  • 34. Optionalのいいところ • 「無効な値」という概念を型にしたことで、 意味論ではなく構文論として扱える • 無効な値を扱う操作が「書けない」文法
  • 35. Optional<T> enum Optional<T> : Reflectable, NilLiteralConvertible { case None case Some(T) ! /// Construct a `nil` instance. init() ! /// Construct a non- `nil` instance that stores `some`. init(_ some: T) ! /// If `self == nil`, returns `nil`. Otherwise, returns `f(self!)`. func map<U>(f: (T) -> U) -> U? ! /// Returns a mirror that reflects `self`. func getMirror() -> MirrorType ! /// Create an instance initialized with `nil`. init(nilLiteral: ()) }
  • 36. Optional<T> • 実体はGeneric Enum • 実はちょっと嘘で、特別扱いを受けている • cf. http://qiita.com/koher/items/ 5dd6ce7427e7ecff4249 • (Failable<T>と同じように)箱です
  • 37. Optional as 箱 • 「Tかもしれない」ではなく、「Tの入る箱」 • 値に触るためには箱を開ける必要がある • binding/coalescing/unwrapping • 箱を開けずに操作する方法もある • mapping/chaining
  • 38. Optional Binding var optionalInt: Int? ! if let int = optionalInt { // int: Int println("optionalInt was (int)") } else { println("optionalInt was nil") } ! var array = [1, 2, 3, 4] while let item = array.last { // item: Int println("(item)") array.removeLast() }
  • 39. 箱を開ける: Optional Binding • 値があればtrueに評価されつつ中身を変数に 束縛(bind)してくれる • 一度に一個しかbindできないのは多少不便
  • 40. Optional Coalescing optionalInt ?? 3 ! // ↓と等価: optionalInt != nil ? optionalInt! : 3 ! // あるいは: func coalesce<T>(lhs: T?, rhs: @autoclosure () -> T) -> T { if let lhs = lhs { return lhs } else { return rhs() } } coalesce(optionalInt, 3)
  • 41. 箱を開ける: Optional Coalescing • 開けられたら開ける • 開けられなかったら開けない • かわりにデフォルト値を返す
  • 42. 無理矢理開ける: Forced Unwrapping optionalInt! • 値があればそれが返るが、なければ落ちる • 落ちる可能性があるので使うべきではない • 安全が保証できるなら構わないが……
  • 43. 箱から出さずに進める方法 • Optional mapping • Optional chaining
  • 44. Optional Mapping /// 3倍する関数 func triple(x: Int) -> Int { return x * 3 } ! optionalInt = 2 // 以下はすべて.Some 6 optionalInt.map(triple) map(optionalInt, triple) optionalInt.map({ $0 * 3 })
  • 45. 箱を開けない: Optional Mapping • 箱の中から別の箱の中へ • 値があればそれを移して別の箱(Optional) を作る • nilならばnilを返す
  • 46. Optional Mapping T U T -> U T? U? map(T?, T -> U) -> U?
  • 47. Optional Chaining class A { var b: B? } ! class B { var c: C? } ! class C { var i: Int = 1 } var a: A? = A() // a.b.c.iにアクセスしたい
  • 48. Optional Chaining var a: A? ! if let a = a { if let b = a.b { if let c = b.c { println("We finally find (c.i)!") } } }
  • 49. Optional Chaining var a: A? ! if let i = a?.b?.c?.i { println("Optional chaining makes (i)t easy.") }
  • 50. 箱を開けない: Optional Chaining • 失敗するかもしれない呼び出しを連鎖できる • 失敗すればその先には進まずnil
  • 51. Optional Chaining a?.b?.c?.i A? aはA?型の変数
  • 52. × Optional Chaining a.b A? A?型にbというメンバはない
  • 53. Optional Chaining a?.b?.c?.i A? a?.bという形にする
  • 54. Optional Chaining a?.b?.c?.i A() or nil aはnilかSomeのどちらか
  • 55. Optional Chaining a?.b?.c?.i A() B? or nil Someならばbに触れる
  • 56. Optional Chaining a?.b?.c?.i A() B? or nil nil nilならそれで終わり
  • 57. Optional Chaining a?.b?.c?.i A() B? or nil nil bはB?型の変数
  • 58. Optional Chaining a?.b?.c?.i A() B() C? or or nil nil nil 同じように分岐
  • 59. Optional Chaining a?.b?.c?.i A() B() C() 7 or or or nil nil nil nil 以下同文
  • 60. 書き直してみる extension Optional { func chain<U>(f: T -> U?) -> U? { if let s = self { return f(s) } else { return nil } } } ! if let i = a.chain({ $0.b }).chain({ $0.c }).chain({ $0.i }) { println("(i)") } ! // (正確には↓だが説明は省略(結果は等しくなる)) // let k = a.chain({ $0.b.chain({ $0.c.chain({ $0.i }) }) })
  • 62. Failable Halve Int Int? // 2で割れたら割るけど割れなかったらあきらめる(やる気のない)関数 func failableHalve(x: Int) -> Int? { if x % 2 == 0 { return x / 2 } else { return nil } } ! if let a = failableHalve(x) { println("Halve((x)) = (a)") } else { println("Failed") }
  • 63. Failable Halve Again Int Int? if let a = failableHalve(x) { if let b = failableHalve(a) { println("Halve^2((x)) = (b)") } else { println("Failed") } } else { println("Failed") } Int Int?
  • 64. Failable Halve The 3rd Int Int? if let a = failableHalve(x) { if let b = failableHalve(a) { if let c = failableHalve(b) { println("Halve^3((x)) = (c)") } else { println("Failed") } } else { println("Failed") } } else { println("Failed") } Int Int? Int Int?
  • 65. Failable Halve Chaining Int Int? if let a = failableHalve(x).chain(failableHalve).chain(failableHalve) { println("Halve^3((x)) = (a)") } else { println("Failed") } Int? Int?
  • 66. Failable Halve Chaining Int Int? Int? Int? 24 failableHalve(24) // Some 12 failableHalve(24).chain(failableHalve) // Some 6 failableHalve(24).chain(failableHalve).chain(failableHalve) // Some 3 failableHalve(24).chain(failableHalve).chain(failableHalve).chain(failableHalve) // nil ! 36 failableHalve(36) // Some 18 failableHalve(36).chain(failableHalve) // Some 9 failableHalve(36).chain(failableHalve).chain(failableHalve) // nil failableHalve(36).chain(failableHalve).chain(failableHalve).chain(failableHalve) // nil
  • 68. Optional Chaining T f U? f(t): U?
  • 69. Optional Chaining U g V? T f U? f(t): U?
  • 70. Optional Chaining g f V? T U? f(t).chain(g): V?
  • 71. Optional Chaining g V h W? f V? T U? f(t).chain(g): V?
  • 72. Optional Chaining g h W? f V? T U? f(t).chain(g).chain(h): W?
  • 74. Array Mapping T [T] T -> U U [U] map([T], T -> U) -> [U]
  • 75. Optional Mapping T U T -> U T? U? map( T?, T -> U) -> U?
  • 76. Optional<T>とArray<T> • T型の値が入った箱 • mapを使えば箱の中身だけを写すことができ る
  • 77. Optional as 状況 • ところで、Optionalは「nilかもしれない」と いう状況(context)を表現していた • Arrayはどんな状況を表現している? • →「値が確定していない」
  • 80. 歩く (0, 1) (-1,0) (1, 0) (0,-1) Kyoto
  • 81. 歩く /// 座標 typealias Position = (Int, Int) ! /// 東西南北のどこかへ歩きます func walk(from: Position) -> [Position] { let (x, y) = from return [(x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1)] } ! walk((0, 0)) // [(-1, 0), (1, 0), (0, -1), (0, 1)]
  • 84. Array Chaining T [U] U [V] V [W]
  • 85. Array Chaining extension Array { func chain<U>(f: Element -> [U]) -> [U] { var ys = [U]() for x in self { ys += f(x) } return ys } } ! walk((0, 0)).chain(walk).chain(walk)
  • 88. BFTask doHeavyJobAsync1().continueWithBlock { (task: BFTask!) -> BFTask in let result = task.result as NSString self.resultLabel1.text = result self.resultLabel2.text = "処理中..." return self.doHeavyJobAsync2(result) }.continueWithBlock { (task: BFTask!) -> BFTask in let result = task.result as NSString self.resultLabel2.text = result self.resultLabel3.text = "処理中..." return self.doHeavyJobAsync3(result) }.continueWithBlock { (task: BFTask!) -> AnyObject! in let result = task.result as NSString self.resultLabel3.text = result return nil }
  • 89. BFTask private func doHeavyJobAsync2(prevResult: String) -> BFTask { var completionSource = BFTaskCompletionSource() // 5秒待ちの処理 // 実用的には、AFNetworkingのcompletionブロック等でsetResultするイメージ Util.delay(5, { completionSource.setResult(prevResult + "オワタ") }) return completionSource.task }
  • 90. Task<T> class Task<T: AnyObject> { private let task: BFTask ! init(task: BFTask) { self.task = task } var result: T? { return task.result as? T } func chain<U: AnyObject>(f: (T -> Task<U>)) -> Task<U> { let newTask = task.continueWithBlock { f($0.result as T).task } return Task<U>(task: newTask) } }
  • 91. Taskを返す関数 private func doHeavyJobAsync2(prevResult: NSString) -> Task<NSString> { let source = Source<NSString>() // 5秒待ちの処理 // 実用的には、AFNetworkingのcompletionブロック等でsetResultするイメージ delay(5, { source.setResult(prevResult + "オワタ" as NSString) }) return source.task }
  • 92. Task Chaining T U U V V W 非同期 非同期 非同期
  • 93. Task Chaining func heavyTask1(Void) -> Task<NSString> { return delayedTask(5) { NSLog("First Task (no params)") return "123" as NSString } } ! func heavyTask2(string: NSString) -> Task<NSNumber> { return delayedTask(5) { NSLog("Second Task (param: (string))") return NSNumber(integer: (string as String).toInt() ?? 0) } } ! func heavyTask3(number: NSNumber) -> Task<NSNumber> { return delayedTask(5) { NSLog("Third Task (param: (number))") return NSNumber(integer: number.integerValue * 2) } }
  • 94. Task Chaining NSLog("Start") heavyTask1().chain(heavyTask2).chain(heavyTask3) NSLog("End") 2014-11-27 21:15:24.659 MySweetSwift[18362:314049] Start 2014-11-27 21:15:24.663 MySweetSwift[18362:314049] End 2014-11-27 21:15:29.663 MySweetSwift[18362:314049] First Task (no params) 2014-11-27 21:15:35.136 MySweetSwift[18362:314049] Second Task (param: 123) 2014-11-27 21:15:40.636 MySweetSwift[18362:314049] Third Task (param: 123)
  • 95. まとめ • Optionalはかわいい! • ArrayとOptionalは似ている • すなわち、箱であり、状況である • 非同期処理も同じように見ることができる • Optional Chainingのような書きかたができて、そ れはBFTaskに似ている