phantom typeでコンパイル時に状態チェックする: shibuya.swift lt04

24
Phantom Typeでコンパイル時に 状態チェックをする shibuya.swift #4 Kazuhiro Hayashi

Upload: hayashi-kazuhiro

Post on 13-Apr-2017

266 views

Category:

Technology


1 download

TRANSCRIPT

Page 1: Phantom Typeでコンパイル時に状態チェックする: shibuya.swift lt04

Phantom Typeでコンパイル時に 状態チェックをする

shibuya.swift #4

Kazuhiro Hayashi

Page 2: Phantom Typeでコンパイル時に状態チェックする: shibuya.swift lt04

Phantom Typeとは...

要は型パラメータを使って、 見た目上現れない制約を付けることらしい(?)

Page 3: Phantom Typeでコンパイル時に状態チェックする: shibuya.swift lt04

Phantom Typeとは...

幽霊みたいな型

要は型パラメータを使って、 見た目上現れない制約を付けることらしい(?)

Page 4: Phantom Typeでコンパイル時に状態チェックする: shibuya.swift lt04

Phantom Typeとは...

幽霊みたいな型

Phantom Type

要は型パラメータを使って、 見た目上現れない制約を付けることらしい(?)

Page 5: Phantom Typeでコンパイル時に状態チェックする: shibuya.swift lt04

例えば…

Page 6: Phantom Typeでコンパイル時に状態チェックする: shibuya.swift lt04

Phantom Typeを使った クラス定義

class PokemonStateType {}

class Struggle: PokemonStateType {} // 戦闘状態 class Calm: PokemonStateType {} // 休憩状態

class Pokemon<T: PokemonStateType> { static func use() -> Pokemon<Calm> { return Pokemon<Calm>() } func ready() -> Pokemon<Struggle> { return Pokemon<Struggle>() } }

extension Pokemon where T: Struggle { func attack() {} }

Page 7: Phantom Typeでコンパイル時に状態チェックする: shibuya.swift lt04

Phantom Typeを使った クラス定義

class PokemonStateType {}

class Struggle: PokemonStateType {} // 戦闘状態 class Calm: PokemonStateType {} // 休憩状態

class Pokemon<T: PokemonStateType> { static func use() -> Pokemon<Calm> { return Pokemon<Calm>() } func ready() -> Pokemon<Struggle> { return Pokemon<Struggle>() } }

extension Pokemon where T: Struggle { func attack() {} }

状態を型パラメータとして持つポケモンクラスを作成する

Page 8: Phantom Typeでコンパイル時に状態チェックする: shibuya.swift lt04

Phantom Typeを使った クラス定義

class PokemonStateType {}

class Struggle: PokemonStateType {} // 戦闘可能状態 class Calm: PokemonStateType {} // 戦闘不可状態

class Pokemon<T: PokemonStateType> { static func use() -> Pokemon<Calm> { return Pokemon<Calm>() } func ready() -> Pokemon<Struggle> { return Pokemon<Struggle>() } }

extension Pokemon where T: Struggle { func attack() {} }

ポケモンは「戦闘状態」と「休憩状態」を持つ

class PokemonStateType {}

class Struggle: PokemonStateType {} // 戦闘状態 class Calm: PokemonStateType {} // 休憩状態

class Pokemon<T: PokemonStateType> { static func use() -> Pokemon<Calm> { return Pokemon<Calm>() } func ready() -> Pokemon<Struggle> { return Pokemon<Struggle>() } }

extension Pokemon where T: Struggle { func attack() {} }

Page 9: Phantom Typeでコンパイル時に状態チェックする: shibuya.swift lt04

Phantom Typeを使った クラス定義

class PokemonStateType {}

class Struggle: PokemonStateType {} // 戦闘可能状態 class Calm: PokemonStateType {} // 戦闘不可状態

class Pokemon<T: PokemonStateType> { static func use() -> Pokemon<Calm> { return Pokemon<Calm>() } func ready() -> Pokemon<Struggle> { return Pokemon<Struggle>() } }

extension Pokemon where T: Struggle { func attack() {} }

戦闘状態の時はattack()が実行可能

class PokemonStateType {}

class Struggle: PokemonStateType {} // 戦闘状態 class Calm: PokemonStateType {} // 休憩状態

class Pokemon<T: PokemonStateType> { static func use() -> Pokemon<Calm> { return Pokemon<Calm>() } func ready() -> Pokemon<Struggle> { return Pokemon<Struggle>() } }

extension Pokemon where T: Struggle { func attack() {} }

Page 10: Phantom Typeでコンパイル時に状態チェックする: shibuya.swift lt04

Pokemonクラスを使ってみる

let pokemon = Pokemon.use() pokemon.attack() // -> コンパイル時にエラー pokemon.ready().attack() // -> こっちはコンパイルエラーが出ない

Page 11: Phantom Typeでコンパイル時に状態チェックする: shibuya.swift lt04

Pokemonクラスを使ってみる

let pokemon = Pokemon.create() pokemon.attack() // -> コンパイル時にエラー pokemon.ready().attack() // -> こっちはコンパイルエラーが出ない

型パラメータにCalmが持つ場合、attack()メソッドは呼べない

let pokemon = Pokemon.use() pokemon.attack() // -> コンパイル時にエラー pokemon.ready().attack() // -> こっちはコンパイルエラーが出ない

Page 12: Phantom Typeでコンパイル時に状態チェックする: shibuya.swift lt04

Pokemonクラスを使ってみる

let pokemon = Pokemon.create() pokemon.attack() // -> コンパイル時にエラー pokemon.ready().attack() // -> こっちはコンパイルエラーが出ない

型パラメータにStruggleを持つPokemonの場合、attack()メソッドが呼べる

let pokemon = Pokemon.use() pokemon.attack() // -> コンパイル時にエラー pokemon.ready().attack() // -> こっちはコンパイルエラーが出ない

Page 13: Phantom Typeでコンパイル時に状態チェックする: shibuya.swift lt04

見かけ上は隠された型を使って 状態管理ができた…

Page 14: Phantom Typeでコンパイル時に状態チェックする: shibuya.swift lt04

実用例

Page 15: Phantom Typeでコンパイル時に状態チェックする: shibuya.swift lt04

https://github.com/kazuhiro4949/StringStylizer

Page 16: Phantom Typeでコンパイル時に状態チェックする: shibuya.swift lt04

StringStylizer

• NSAttributedStringを型によって使いやすくしたライブラリを作りました

• Phantom Typeによって範囲選択中とスタイル適用中の状態を切り替える

Page 17: Phantom Typeでコンパイル時に状態チェックする: shibuya.swift lt04

StringStylizerlet msg = “shibuya.swift".stylize() .range(0..<7) .color(0x009911).font(.HelveticaNeue) .range(8..<UInt.max).color(0xaa22cc).font(.HelveticaNeue_Bold).attr

• テキストに対してメソッドチェーンでスタイルを適用していく

• NSAttributedStringと同様、任意の範囲に対してスタイルを適用するという流れでつくる

Page 18: Phantom Typeでコンパイル時に状態チェックする: shibuya.swift lt04

状態遷移図としてまとめると

型パラメータを切り替えることで状態を行ったり来たりしながら、適切なメソッドをコールする

Page 19: Phantom Typeでコンパイル時に状態チェックする: shibuya.swift lt04

状態によって コンパイルエラーが起きる

// 範囲選択したあとで何かスタイルを適用していないとコンパイルエラー label.attributedText = "shibuya.swift".stylize().range(0..<7).attr

// 範囲選択後に色を適用しているのでコンパイルに通る

label.attributedText = "shibuya.swift".stylize().range(0..<7).color(0x009911).attr

例えばUILabelに対して、文字列のある範囲のみスタイルを適用して代入する場合を考える

Page 20: Phantom Typeでコンパイル時に状態チェックする: shibuya.swift lt04

状態によって コンパイルエラーが起きる

// 範囲選択したあとで何かスタイルを適用していないとコンパイルエラー label.attributedText = "shibuya.swift".stylize().range(0..<7).attr

// 範囲選択後に色を適用しているのでコンパイルに通る

label.attributedText = "shibuya.swift".stylize().range(0..<7).color(0x009911).attr

範囲選択をしている時は、その後で必ずスタイルの適用をする必要がある

Page 21: Phantom Typeでコンパイル時に状態チェックする: shibuya.swift lt04

状態によって コンパイルエラーが起きる

// 範囲選択したあとで何かスタイルを適用していないとコンパイルエラー label.attributedText = "shibuya.swift".stylize().range(0..<7).attr

// 範囲選択後に色を適用しているのでコンパイルに通る

label.attributedText = "shibuya.swift".stylize().range(0..<7).color(0x009911).attr

スタイルを適用した後は、別のスタイルを適用するかNSAttributedStringとして代入できる

Page 22: Phantom Typeでコンパイル時に状態チェックする: shibuya.swift lt04

Phantom Type まとめ

• 型パラメータによってオブジェクトの振る舞いに制約を与えられる

• コンパイラによって正しい振る舞いをしているかどうかチェックできる

• 間違ってたらごめん

Page 23: Phantom Typeでコンパイル時に状態チェックする: shibuya.swift lt04

もし何かうまい使い方があれば 教えて下さいm(_ _)m

Page 24: Phantom Typeでコンパイル時に状態チェックする: shibuya.swift lt04

参考資料• Swift で Phantom Type (幽霊型)

• http://qiita.com/taketo1024/items/71e3272211f08d7e0cde

• Functional Snippet #13: Phantom Types

• https://www.objc.io/blog/2014/12/29/functional-snippet-13-phantom-types/

• kazuhiro4949/StringStylizer

• https://github.com/kazuhiro4949/StringStylizer