phantom typeでコンパイル時に状態チェックする: shibuya.swift lt04
TRANSCRIPT
![Page 1: Phantom Typeでコンパイル時に状態チェックする: shibuya.swift lt04](https://reader033.vdocuments.net/reader033/viewer/2022051101/58eeb8851a28ab271c8b46d1/html5/thumbnails/1.jpg)
Phantom Typeでコンパイル時に 状態チェックをする
shibuya.swift #4
Kazuhiro Hayashi
![Page 2: Phantom Typeでコンパイル時に状態チェックする: shibuya.swift lt04](https://reader033.vdocuments.net/reader033/viewer/2022051101/58eeb8851a28ab271c8b46d1/html5/thumbnails/2.jpg)
Phantom Typeとは...
要は型パラメータを使って、 見た目上現れない制約を付けることらしい(?)
![Page 3: Phantom Typeでコンパイル時に状態チェックする: shibuya.swift lt04](https://reader033.vdocuments.net/reader033/viewer/2022051101/58eeb8851a28ab271c8b46d1/html5/thumbnails/3.jpg)
Phantom Typeとは...
幽霊みたいな型
要は型パラメータを使って、 見た目上現れない制約を付けることらしい(?)
![Page 4: Phantom Typeでコンパイル時に状態チェックする: shibuya.swift lt04](https://reader033.vdocuments.net/reader033/viewer/2022051101/58eeb8851a28ab271c8b46d1/html5/thumbnails/4.jpg)
Phantom Typeとは...
幽霊みたいな型
Phantom Type
要は型パラメータを使って、 見た目上現れない制約を付けることらしい(?)
![Page 5: Phantom Typeでコンパイル時に状態チェックする: shibuya.swift lt04](https://reader033.vdocuments.net/reader033/viewer/2022051101/58eeb8851a28ab271c8b46d1/html5/thumbnails/5.jpg)
例えば…
![Page 6: Phantom Typeでコンパイル時に状態チェックする: shibuya.swift lt04](https://reader033.vdocuments.net/reader033/viewer/2022051101/58eeb8851a28ab271c8b46d1/html5/thumbnails/6.jpg)
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](https://reader033.vdocuments.net/reader033/viewer/2022051101/58eeb8851a28ab271c8b46d1/html5/thumbnails/7.jpg)
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](https://reader033.vdocuments.net/reader033/viewer/2022051101/58eeb8851a28ab271c8b46d1/html5/thumbnails/8.jpg)
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](https://reader033.vdocuments.net/reader033/viewer/2022051101/58eeb8851a28ab271c8b46d1/html5/thumbnails/9.jpg)
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](https://reader033.vdocuments.net/reader033/viewer/2022051101/58eeb8851a28ab271c8b46d1/html5/thumbnails/10.jpg)
Pokemonクラスを使ってみる
let pokemon = Pokemon.use() pokemon.attack() // -> コンパイル時にエラー pokemon.ready().attack() // -> こっちはコンパイルエラーが出ない
![Page 11: Phantom Typeでコンパイル時に状態チェックする: shibuya.swift lt04](https://reader033.vdocuments.net/reader033/viewer/2022051101/58eeb8851a28ab271c8b46d1/html5/thumbnails/11.jpg)
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](https://reader033.vdocuments.net/reader033/viewer/2022051101/58eeb8851a28ab271c8b46d1/html5/thumbnails/12.jpg)
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](https://reader033.vdocuments.net/reader033/viewer/2022051101/58eeb8851a28ab271c8b46d1/html5/thumbnails/13.jpg)
見かけ上は隠された型を使って 状態管理ができた…
![Page 14: Phantom Typeでコンパイル時に状態チェックする: shibuya.swift lt04](https://reader033.vdocuments.net/reader033/viewer/2022051101/58eeb8851a28ab271c8b46d1/html5/thumbnails/14.jpg)
実用例
![Page 15: Phantom Typeでコンパイル時に状態チェックする: shibuya.swift lt04](https://reader033.vdocuments.net/reader033/viewer/2022051101/58eeb8851a28ab271c8b46d1/html5/thumbnails/15.jpg)
https://github.com/kazuhiro4949/StringStylizer
![Page 16: Phantom Typeでコンパイル時に状態チェックする: shibuya.swift lt04](https://reader033.vdocuments.net/reader033/viewer/2022051101/58eeb8851a28ab271c8b46d1/html5/thumbnails/16.jpg)
StringStylizer
• NSAttributedStringを型によって使いやすくしたライブラリを作りました
• Phantom Typeによって範囲選択中とスタイル適用中の状態を切り替える
![Page 17: Phantom Typeでコンパイル時に状態チェックする: shibuya.swift lt04](https://reader033.vdocuments.net/reader033/viewer/2022051101/58eeb8851a28ab271c8b46d1/html5/thumbnails/17.jpg)
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](https://reader033.vdocuments.net/reader033/viewer/2022051101/58eeb8851a28ab271c8b46d1/html5/thumbnails/18.jpg)
状態遷移図としてまとめると
型パラメータを切り替えることで状態を行ったり来たりしながら、適切なメソッドをコールする
![Page 19: Phantom Typeでコンパイル時に状態チェックする: shibuya.swift lt04](https://reader033.vdocuments.net/reader033/viewer/2022051101/58eeb8851a28ab271c8b46d1/html5/thumbnails/19.jpg)
状態によって コンパイルエラーが起きる
// 範囲選択したあとで何かスタイルを適用していないとコンパイルエラー 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](https://reader033.vdocuments.net/reader033/viewer/2022051101/58eeb8851a28ab271c8b46d1/html5/thumbnails/20.jpg)
状態によって コンパイルエラーが起きる
// 範囲選択したあとで何かスタイルを適用していないとコンパイルエラー 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](https://reader033.vdocuments.net/reader033/viewer/2022051101/58eeb8851a28ab271c8b46d1/html5/thumbnails/21.jpg)
状態によって コンパイルエラーが起きる
// 範囲選択したあとで何かスタイルを適用していないとコンパイルエラー 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](https://reader033.vdocuments.net/reader033/viewer/2022051101/58eeb8851a28ab271c8b46d1/html5/thumbnails/22.jpg)
Phantom Type まとめ
• 型パラメータによってオブジェクトの振る舞いに制約を与えられる
• コンパイラによって正しい振る舞いをしているかどうかチェックできる
• 間違ってたらごめん
![Page 23: Phantom Typeでコンパイル時に状態チェックする: shibuya.swift lt04](https://reader033.vdocuments.net/reader033/viewer/2022051101/58eeb8851a28ab271c8b46d1/html5/thumbnails/23.jpg)
もし何かうまい使い方があれば 教えて下さいm(_ _)m
![Page 24: Phantom Typeでコンパイル時に状態チェックする: shibuya.swift lt04](https://reader033.vdocuments.net/reader033/viewer/2022051101/58eeb8851a28ab271c8b46d1/html5/thumbnails/24.jpg)
参考資料• 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