>100 Views
September 16, 16
スライド概要
iOSDC でも話題に登った Phantom Type という存在が気になって、その得体知れないものを知るために、闇雲に特徴を探ってみたお話です。
※ Docswell での公開に移行する直前の Slideshare での閲覧数は 2,945 でした。
正統派趣味人プログラマー。プログラミングとは幼馴染です。
͡Ίͯ1IBOUPNͱૺ۰ͯ͠ ҋӢʹಆ͍ΛΜͰΈͨ Վ࠲بUFDI 4XJGUϓϩάϥϛϯάษڧձPO &;/&5۽୩༑ IUUQF[OFUKQ 4XJGU
۽୩༑ 5PNPIJSP,VNBHBJ ⾣ 4XJGU͕ޠݴେ͖Ͱ͢ʂ ⾣ ΈΜͳͰָ͠Ήษڧձ͕େ͖Ͱ͢ʂ ⾣ ϓϩάϥϛϯάͷָ͠͞Λ͍͖͍͑ͯͨɻ !FT@LVNBHBJ UPNPIJSPLVNBHBJ $PEF1JFDFGPSNBD04 IUUQF[OFUKQ 9DPEFపఈղઆ
$PEF1JFDFGPSNBD04 ษڧձΛָ͠ΉΞϓϦ ⾣ ιʔείʔυΛ5XJUUFSͱ(JTUʹಉ࣌ߘ ⾣ ίʔυཝΛۭʹ͢Εɺී௨ʹπΠʔτՄೳ ⾣ ϋογϡλάΛઃఆͰ͖Δʗ͚Εͳ͍ #kbkz_tech
ษڧձΛ։࠵͍ͯ͠·͢ɻ Θ͍Θ͍ɺָ͘͠ɺ ΈΜͳͰޠΒ͑ΔॴΛࢦͯ͠ ԣJ1IPOF։ൃऀษڧձ ୈճ݄ͷ։࠵ʢඪʣ ZJEFW ˏԣɾഅंಓ IUUQTBUOEPSHHSPVQTZJEFW ΧδϡΞϧ4XJGUษڧձ ΈΜͳͰ4XJGU ෮शձ DTXJGU !ԣɾ੨༿ NJOOB@EF@TXJGU ˏौ୩ ୈճΛ݄ʹ։࠵ ୈճΛ݄ʹ։࠵ IUUQTBUOEPSHHSPVQTDTXJGU IUUQDTXJGUDPOOQBTTDPN
ωοτϥδΦ Ќ์ૹ։࢝ɻ ϓϩάϥϛϯά͕େ͖ͳ̎ਓͰɺ ϓϩάϥϜޠָ͍ͯͭ͘͠ʹޠݴΒ͏൪ NPPLNPPLSBEJP Ќ൛ ϜοΫϜοΫϥδΦ ۽୩ͱ៸໘͕ϓϩάϥϛϯάίʔυͷ͔Β ௌ͑ͯ͘͜ΔʹࣖΛָ͚ͯ͠ΉϥδΦ ୈʜ݄ΑΓ์ૹ։࢝ୈʜ݄ͷ์ૹ༧ఆ IUUQNPPLNPPLSBEJPDPNB
͡Ίͯ1IBOUPNͱૺ۰ͯ͠ ҋӢʹಆ͍ΛΜͰΈͨ Πϥετग़లIUUQقઅͷΠϕϯτDPN
ɾɾɾ
Ͱ៸໘͞Μ͕ັͤͯ͘Εͨ 1IBOUPN5ZQF IUUQTXXXZPVUVCFDPNXBUDI WORG)M+--D
ͨͱ͑ɻ QSFQBSF͖̍Γʹ͍ͯͩ͘͠͞ʂ ⾣ ࣮ߦ੍ޚɺϥϯλΠϜͰߦ͏ͷ͕ৗࣝɻ ⾣ ઃऀܭϥϯλΠϜͰͦͷҙਤΛ͍ࣔͯͨ͠ɻ class HogeOperation { private(set) var isPrepared = false func prepare() { guard !isPrepared else { fatalError("prepare を複数回呼ばないこと") } isPrepared = true } }
៸໘͞Μ͕ͯͤ͘ݟΕͨ 1IBOUPN5ZQFͱ͍͏ղ๏ ⾣ ͦܕͷͷʹঢ়ଶΛͯࠐΈɺίʔυΛ੍͢ޚΔ ⾣ ઃऀܭͷҙਤ͕ίʔσΟϯά࣌ʹϓϩάϥϚʔʹΘΔ // Init 状態の HogeOperation を生成 let operation = HogeOperation<Init>() // 最初は prepared を呼べて準備完了したものを取得可能 let preparedOp = operation.prepared() // 準備が終われば prepared はビルドエラーで実行不可 preparedOp.prepared() b1SFQBSFE`JTOPUBTVCUZQFPGb*OJU`
1IBOUPN5ZQFͷ࡞Γํ
1IBOUPN5ZQFͷ࡞Γํ ঢ়ଶΛઃ͢ܭΔ ⾣ ঢ়ଶ͝ͱʹΫϥεΛఆٛ͢Δ ⾣ ϓϩτίϧΛఴ͑ΕɺऔΓಘΔঢ়ଶ͕໌֬ʹͳΔ // これで "操作状態を表現する型" を表現 protocol OperationState {} // 操作状態ごとにクラスを定義 class Init: OperationState {} class Prepared: OperationState {}
1IBOUPN5ZQFͷ࡞Γํ ܕΛઃ͢ܭΔ ⾣ ܕύϥϝʔλʔͰঢ়ଶΛ࣋ͨͤΔ ⾣ ঢ়ଶܕΛઆ໌͢Δ͚ͩͰɺ෦ͰΘͳ͍ // 型パラメーターで型に状態を付与 class HogeOperation<State: OperationState> { /* 今回は内部で、 型パラメーターを使わないのがポイント */ }
1IBOUPN5ZQFͷ࡞Γํ ঢ়ଶ͝ͱʹػೳΛઃ͢ܭΔ ⾣ ੍͖֦ܕுͰɺঢ়ଶ͝ͱʹػೳΛ࣮͢Δ ⾣ ྫ͑ɺ४උલʹ४උ࣮ߦͷػೳΛඋ͑Δ extension HogeOperation where State: Init { // 準備を実行し、準備完了状態のインスタンスを返す func prepared() -> HogeOperation<Prepared> {…} } extension HogeOperation where State: Prepared { // 目的の操作を実行する func execute() {…} }
1IBOUPN5ZQF
1IBOUPN5ZQF ঢ়ଶʹԠͯ͡ɺػೳ͕มΘΔ ⾣ ܕύϥϝʔλʔΛػೳସ͚ͩʹ͏ͷ͕ϙΠϯτ ⾣ -JWF*TTVFTͷஈ֊ͰίʔσΟϯάϛεʹ͚ͮؾΔ let operation = HogeOperation<Init>() // Init 状態では、まだ execute は存在しない operation.execute() b*OJU`JTOPUBTVCUZQFPGb1SFQBSFE` // prepared を呼ぶことで Prepared 状態のものを取得 let preparedOp = operation.prepared() // Prepared 状態には、もう prepared は存在しない preparedOp.prepared() b1SFQBSFE`JTOPUBTVCUZQFPGb*OJU`
ϏϧυλΠϜʹঢ়ଶఆͱ͔ɺ͍͢͝ʜʂ
͔͠͠ɺ͜ͷಘମͷΕͳ͍ίʔυʜ 1IBOUPN5ZQFʜԿऀͳΜͩɻ
1IBOUPN5ZQFΛΒͳ͍͕ࣗ ͨͩҋӢʹఁͯ͠ɺۭ͍ͯ͘͠ޠ ಆ͏ʹɺ·ͣɺఢΛΒͳ͚ΕͳΒͳ͍ʜʂ
ࠜຊΛఁ
1IBOUPN5ZQFͬͯͳΜͩΖ͏ʁ
1IBOUPN5ZQFͬͯͳΜͩΖ͏ʁ ܕύϥϝʔλʔͰɺܕΛ੍͢ޚΔ ⾣ ܕύϥϝʔλʔϏϧυ͕ࡁΜͩΒऴΘΓ ⾣ ·ΔͰ༓ྶΈ͍ͨʹফ໓͢Δ͔Β1IBOUPN5ZQFʁ let operation = HogeOperation<Init>() // HogeOperation<Init> 型だから prepared が呼べる let preparedOp: HogeOperation<Prepared>() = operation.prepared() // HogeOperation<Prepared> 型だから execute が呼べる preparedOp.execute() // Init クラスや Prepared クラスを、実行時には使わない // ビルドの段階で、もう役目が済んでいる
༓ྶܕʜʁ
༓ྶܕʜʁ Ϗϧυޙɺ༓ྶΈ͍ͨʹফ໓͢Δʁ ⾣ ·Δ͑ݟͷΑ͏͕ͩʜ ⾣ ϥϯλΠϜͰɺϝλλΠϓΛऔಘͰ͖Δ let operation = HogeOperation<Init>() let preparedOp = operation.prepared() let type1 = type(of: operation) )PHF0QFSBUJPO*OJU5ZQF let type2 = type(of: preparedOp) )PHF0QFSBUJPO1SFQBSFE5ZQF type1 == type2 // false
༓ྶܕʜʁ ϏϧυͰɺΛऴ͑Δͱ͍͏ҙຯ ⾣ ܕύϥϝʔλʔ͕ɺϏϧυ࣌ͷػೳସ͚ͩΛ୲͏ ⾣ ΠϯελϯεมͰΘͣɺ࣮ߦ࣮࣭࣌ແҙຯ // 型パラメーターで型に状態を付与 class HogeOperation<State: OperationState> { /* 内部では、型パラメーターを使っていない 型の在り方を説明するためだけに使っている */ ͨͩɺ͏ଆʹͱͬͯɺܕύϥϝʔλʔΛ ෦ͰͬͯΔ͔ؔ৺ͳ͍͠ɺ֎͔ΒͯݟΘΒͳ͍
δΣωϦοΫܕͷԠ༻ʁ
δΣωϦοΫܕͷԠ༻ʁ ܕύϥϝʔλʔ͕ҧ͑ɺҧ͏ܕʁ ⾣ "SSBZ4USJOHͱ"SSBZ*OUɺผͷ͋ͰܕΔ ⾣ 1IBOUPN5ZQFʹग़⁊͏·Ͱ৴͍ͯͨ͡ /// Phantom Type に出逢う前の認識 // Array はテンプレート的なもので… struct Array<Element> { } // 型パラメーターによって、異なる型になる let values: Array<Int> = Array<String>() DBOOPUDPOWFSUWBMVFPGUZQF"SSBZ*OU UPTQFDJpFEUZQF"SSBZ4USJOH
δΣωϦοΫ͍ͳ͘ྑͯ͘ͳͰܕʁ ϙϦϞʔϑΟζϜͰදͯ͠ݱΈΔ
ϙϦϞʔϑΟζϜͰදͯ͠ݱΈΔ ঢ়ଶͷఆٛඞཁͳ͍ ⾣ 1IBOUPN5ZQFΈ͍ͨͳঢ়ଶఆٛෆཁ ⾣ ঢ়ଶɺϙϦϞʔϑΟζϜͰද͢ݱΔ // protocol OperationState {} // class Init: OperationState {} // class Prepared: OperationState {}
ϙϦϞʔϑΟζϜͰදͯ͠ݱΈΔ جఈΫϥεͰɺάϧʔϓΛද͢ݱΔ ⾣ جఈΫϥεʹɺ෦σʔλͱڞ௨ػೳΛඋ͑Δ ⾣ ܕύϥϝʔλʔ࣋ͨͳ͍ class Operation { fileprivate var data: OperationData fileprivate init(data: OperationData) { self.data = data } func mob() {} }
ϙϦϞʔϑΟζϜͰදͯ͠ݱΈΔ ಋग़ΫϥεͰɺঢ়ଶΛද͢ݱΔ ⾣ ঢ়ଶ͝ͱʹɺಋग़ΫϥεΛ࣮͢Δ ⾣ ֤ঢ়ଶͰݻ༗ͷػೳɺಋग़Ϋϥεʹ࣮͢Δ class OperationInit: Operation { convenience init() {…} func prepared() -> OperationPrepared {…} } class OperationPrepared: Operation { func execute() {…} }
ϙϦϞʔϑΟζϜͰදͯ͠ݱΈΔ ⾣ ࣮ͷҹมΘΔ͕ɺಈ࡞తʹ΄΅ಉ͜͡ͱ ⾣ Ͱܕͷঢ়ଶ۠ผ͕ෆࢥ͕ٞͩɺ࣮ख๏Ұൠత let operation = OperationInit() let preparedOp = operation.prepared() preparedOp.execute()
δΣωϦοΫ͍ͳ͘ྑͯ͘ͳͰܕʁ ผʹɺܧঝ͠ͳ͍͍ͯ͘
ผʹɺܧঝ͠ͳ͍͍ͯ͘ ͻͱͭͷঢ়ଶΛɺ͋ΔͰܕදݱ ⾣ ෦σʔλͱڞ௨ػೳΛඋ͑Δ ⾣ ͜ͷঢ়ଶʹݶΓɺ͑ΔػೳΛ࣮͢Δ struct OperationInit { fileprivate var data: OperationData fileprivate init(data: OperationData) {…} // 共通機能 func mob() {} // 固有の機能 init() {…} func prepared() -> OperationPrepared {…} }
ผʹɺܧঝ͠ͳ͍͍ͯ͘ ͏ͻͱͭͷঢ়ଶΛɺผͷͰܕදݱ ⾣ ͪ͜Βʹɺ෦σʔλͱڞ௨ػೳΛඋ͑Δ ⾣ Ϋϥεܧঝͱҧͬͯɺڞ௨ػೳΛҾ͖͍ͳ͛ܧ struct OperationPrepared { fileprivate var data: OperationData fileprivate init(data: OperationData) {…} // 共通機能 func mob() {} // 固有の機能 func execute() {…} }
ผʹɺܧঝ͠ͳ͍͍ͯ͘ ⾣ ͍উखɺ͜Ε·ͰͷͱมΘΒͳ͍ ⾣ ͕ܕಠཱ͢ΔͨΊɺఆٛཧղ͠ʹ͘͘ͳΔҹ let operation = OperationInit() let preparedOp = operation.prepared() preparedOp.execute()
ผʹɺܧঝ͠ͳ͍͍ͯ͘ άϧʔϓੑΛɺϓϩτίϧͰදݱ ⾣ ϓϩτίϧΛͯΕɺάϧʔϓੑද͖ͰݱΔ ⾣ ͨͩ͠༻ऀɺఆٛΛಡ·ͳ͍ͱ͍ͳ͚ͮؾ protocol Operation {} struct OperationInit: Operation { … } struct OperationPrepared: Operation { … }
δΣωϦοΫ͍ͳ͘ྑͯ͘ͳͰܕʁ ωετͰܕදͯ͠ݱΈΔ
ผʹɺܧঝ͠ͳ͍͍ͯ͘ άϧʔϓੑΛɺωετͰܕදݱ ⾣ ܕͷωετͰɺάϧʔϓੑද͖ͰݱΔ ⾣ ݁ہɺঢ়ଶ͝ͱʹ͕ܕಠཱ͢ΔͳΒ༗Γͳબ struct Operation { struct Init { … } struct Prepared { … } }
ผʹɺܧঝ͠ͳ͍͍ͯ͘ ⾣ 0QFSBUJPO*OJU͕0QFSBUJPO*OJUʹͳ͚ͬͨͩ ⾣ ͜Ε͚ͩͰɺ͏ଆʹάϧʔϓ͕ײΘΔ let operation = Operation.Init() let preparedOp = operation.prepared() preparedOp.execute()
δΣωϦοΫ͍ͳ͘ྑͯ͘ͳͰܕʁ ωετͱܕϙϦϞʔϑΟζϜͰදͯ͠ݱΈΔ
ωετͱܕϙϦϞʔϑΟζϜͰදͯ͠ݱΈΔ جఈΫϥεͰɺάϧʔϓΛද͢ݱΔ ⾣ جఈΫϥεʹɺ෦σʔλͱڞ௨ػೳΛඋ͑Δ ⾣ ͜ΕΛɺʹ͑ݟΔάϧʔϓͱͯ͠͏ class Operation { fileprivate var data: OperationData fileprivate init(data: OperationData) { self.data = data } func mob() {} }
ωετͱܕϙϦϞʔϑΟζϜͰදͯ͠ݱΈΔ άϧʔϓੑΛɺωετͰܕදݱ ⾣ جఈΫϥεʹɺ֤ঢ়ଶΛωετͯ͠ͱܕఆٛ ⾣ ωετݩͷΫϥεΛɺωετઌͰܧঝ͢Δ extension Operation { class Init: Operation { … } class Prepared: Operation { … } }
ωετͱܕϙϦϞʔϑΟζϜͰදͯ͠ݱΈΔ ⾣ ωετͰܕɺར༻ऀʹάϧʔϓੑΛࣔͤΔ ⾣ ڞ௨ػೳΛجఈΫϥεʹ࣮͠ɺҾ͖͛ܧΔ let operation = Operation.Init() let preparedOp = operation.prepared() preparedOp.execute()
ࠓ͋ΔࣝͰߟ͚͑ͨͩͰ 1IBOUPN5ZQF͕௫Ίͯ͜ͳ͍ʜ
1IBOUPN5ZQFΛଊ͑Δʹ ҙࣝվֵ͕ඞཁͦ͏ ͦ͏͠ͳ͚Ε1IBOUPN5ZQF͕ͻͱͭͷͯ͑͘ݟʹܕΕͳ͍
ಛΛఁ
ͳʹؾΔͷɺܕͷಉҰੑ 1IBOUPN5ZQFҎ֎ɺ࣮֬ʹҧ͏Ͱܕද͍ͯ͠ݱΔ
ҙࣝվֵ͕ඞཁͦ͏ ֦ܕு͕ඳ͘ੈք ⾣ ੍͖֦ܕுͰɺಛఆ݅ԼͷৼΔ͍͕มΘΔ ⾣ ͜ΕΒɺٛతʹಉ͡ͱܕଊ͑ͯྑ͍ʁ class HogeOperation<State: OperationState> { func mob() {…} } extension HogeOperation where State: Init { func prepared() -> HogeOperation<Prepared> {…} } extension HogeOperation where State: Prepared { func execute() {…} }
ҙࣝվֵ͕ඞཁͦ͏ ಉܾ͡ͱͩܕఆ͚ͮΔཧ༝ʹͳΔʁ ⾣ ͦΜͳதɺ1IBOUPN5ZQFͰͷڍಈ͕ͨͬͳʹؾ ⾣ ະ࣮ͷཧ༝͕lαϒλΠϓͰͳ͍z͔Βʜʁ let operation = HogeOperation<Init>() operation.execute() *OJUJTOPUBTVCUZQFPG1SFQBSFE ผͰ֦ு͞ΕͨϝιουͷଘࡏΛɺೝ͍ࣝͯ͠Δ༷ࢠ
ҙࣝվֵ͕ඞཁͦ͏ Ϋϥεܧঝͷ߹ɺΤϥʔ͕ҟͳΔ ⾣ ΫϥεܧঝͰ࣮͍ͨͯ͠ݱ߹ɺશʹଘࡏ͠ͳ͍ ⾣ ผͷ͔ͩܕΒ࣮͕ͳ͍ɺࣗવͳಈ͖ let operation = OperationInit() operation.execute() WBMVFPGUZQF0QFSBUJPO*OJUIBTOPNFNCFS FYFDVUFPQFSBUJPOFYFDVUF δΣωϦοΫܕɺٛʹಉ͡ͱܕΈͳͯ͠ྑͦ͞͏
ಉ͡ͱܕΈͳͯ͠ྑͦ͞͏ ͨͩ͠ɺΠϯελϯεશʹผ
ͨͩ͠ɺΠϯελϯεશʹผ ܕύϥϝʔλʔ͕ҧ͑ɺ࣮ମҟͳΔ ⾣ ҟͳΔ࣮ମΛɺಉ͡มͰѻ͑ͳ͍ ⾣ 1IBOUPN5ZQFͷ߹ɺಛʹ͕͜͜ॏཁʹͳΓͦ͏ // 準備前と準備後を、自分自身や同じ変数に書き戻せない var operation = HogeOperation<Init>() operation = operation.prepared() DBOOPUBTTJHOWBMVFPGUZQF)PHF0QFSBUJPO1SFQBSFE UPUZQF)PHF0QFSBUJPO*OJU ܕಉ͡ͰɺΠϯελϯεΛಉҰࢹ͢Δͷ͍͠
ͨͩ͠ɺΠϯελϯεશʹผ
ಉҰࢹ͢Δͱɺແ͠ʹͳΔ
⾣ ϓϩτίϧͰઆ໌͢ΕɺಉҰࢹͰ͖Δ
⾣ ͨͩ͠1IBOUPN5ZQFͷྑ͞શʹࣦΘΕΔ
protocol Operation {}
class HogeOperation<State: OperationState>: Operation {
}
var op: Operation
op = HogeOperation<Init>()
op = (op as! HogeOperation<Init>).prepared()
op = (op as! HogeOperation<Prepared>).execute()
ঢ়ଶͱͯ͠อ࣋͢ΔΑΓɺྲྀ͢ͷʹ͘ʁ
ঢ়ଶΛอଘ͢ΔΑΓɺྲྀ͢ͷʹ͘ʁ ͨͱ͑ɺෳࡶͳॳظԽ͕ඞཁͳͱ͖ ⾣ େͳҙͷύϥϝʔλʔ͕͋Δͱ͖ ⾣ ΠχγϟϥΠβʔͰͷॳظԽɺҾ͕େʹͳΔ class Driver { init(channel: Int? = nil, volume: Int? = nil, pan: Int? = nil, format: Format? = nil, route: Route? = nil) { } } // 設定項目に何があるかや、設定順番に気を使う let driver = Driver(volume: 10, format: ulaw, route: .speaker)
ঢ়ଶΛอଘ͢ΔΑΓɺྲྀ͢ͷʹ͘ʁ 1IBOUPN5ZQFͰྲྀΕΛ࡞Δʜ ⾣ ॳظઃఆͱ४උྃΛঢ়ଶͰදݱ ⾣ ઃఆͱར༻Λ໌֬ʹͰ͖Δ // 本体のクラスを Phantom Type で定義して… class Driver<State> where State: AudioState { } // 準備が整ったときの機能を実装し… extension Driver where State: Ready { func start() { … } }
ঢ়ଶΛอଘ͢ΔΑΓɺྲྀ͢ͷʹ͘ʁ 1IBOUPN5ZQFͰྲྀΕΛ࡞Δʜ ⾣ ॳظઃఆͱ४උྃΛঢ়ଶͰදݱ ⾣ ઃఆͱར༻Λ໌֬ʹͰ͖Δ // 初期化中にできることを規定すると… extension Driver where State: Setup { func prepared() -> Driver<Ready> { … } func set(channel: Int) -> Driver { return self } func set(volume: Int) -> Driver { return self } func set(pan: Int) -> Driver { return self } func set(format: Format) -> Driver { return self } func set(route: Route) -> Driver { return self } }
ঢ়ଶΛอଘ͢ΔΑΓɺྲྀ͢ͷʹ͘ʁ 1IBOUPN5ZQFͰྲྀΕΛ࡞Δ ⾣ ॳظઃఆͱ४උྃΛঢ়ଶͰදݱ ⾣ ϝιουνΣΠϯͷख๏ͰྲྀΕΛ࡞Δ // 初期設定では、順番を気にせず設定できる・補完が効く let driver = Driver<Setup>() .set(format: ulaw) .set(volume: 10) .set(route: .speaker) .prepared() // ここで Driver<Ready>() を返す // 設定完了を宣言 (prepared) して、使い始める driver.start()
ঢ়ଶΛอଘ͢ΔΑΓɺྲྀ͢ͷʹ͘ʁ ෳࡶͳྲྀΕͰޠݴಋ͚Δ ⾣ ϝιουνΣΠϯͱͰܕಋ͘ɺঢ়ଶભҠ ⾣ ຊޠ༧ଌมΈ͍ͨʹɺิྲྀͰิީΕΛ༠ಋ let driver = Driver<Setup>() .format // → Driver<FormatSetup> .set(sampleRate: 44100) .set(channelsPerFrame: 2) .general // → Driver<GeneralSetup> .set(volume: 10) .set(route: .speaker) .prepared() // → Driver<Ready>
ঢ়ଶΛอଘ͢ΔΑΓɺྲྀ͢ͷʹ͘ʁ 1IBOUPN5ZQFͰͳͯ͘ಋ͚Δ ⾣ ఆٛଆɺ1IBOUPN5ZQFͩͱάϧʔϓ͕ײग़ͯྑ͍ ⾣ ͏ଆɺܕਪͷ͓͔͛ͰɺܕͳΜͰྑ͍ let driver = Driver.setup() // → DriverSetup .format .set(sampleRate: 44100) .set(channelsPerFrame: 2) // → FormatSetup .general .set(volume: 10) .set(route: .speaker) // → AudioSetup .prepared() // → Driver
ίϯςΩετํࣜͰ࣮ݱՄೳ
ίϯςΩετํࣜͰ࣮ݱՄೳ ઃఆ߲ΛߏମͰ༻ҙ͢Δ ⾣ ઃఆͷྲྀΕɺσʔλߏͰಋ͘ ⾣ Կ͔ॳظΛ࣋ͨͤΕɺҙ߲ʹͰ͖Δ // 設定項目を、初期値を持った構造体で用意して… struct Description { var channel: Int = default var volume: Int = default var pan: Int = default var format: Format = default var route: Route = default }
ίϯςΩετํࣜͰ࣮ݱՄೳ ߏମʹΛͦΖ͑Δ ⾣ ߏମΛΠϯελϯεԽͯ͠ɺΛͦΖ͑Δ ⾣ σʔλߏͷ௨Γʹɺิ͘ޮ͕ let description = Description() description.format.sampleRate = 44100 description.format.channelsPerFrame = 2 description.volume = 10 description.route = .speaker
ίϯςΩετํࣜͰ࣮ݱՄೳ ઃఆ߲Λఴ͑ͯɺΠϯελϯεΛ࡞Δ ⾣ ઃఆ߲Λఴ͑ͯॳظԽͯ͠ɺ ⾣ ܕΛঢ়ଶ੍ͳ͍ͳ͠ޚΒɺ͜Ε͕γϯϓϧ // Driver は Description で初期化するようにして… class Driver { init(description: Description) { … } } // 設定項目を渡して、初期化を完成する let driver = Driver(description: description)
ܕͷৼସΛߟ͑Δ
ঢ়ଶͱͯ͠อ͍࣋ͨ͠߹
ঢ়ଶͱͯ͠อ͍࣋ͨ͠߹ χϡʔτϥϧΛنఆ͠ɺͦΕΛอ࣋͢Δ ⾣ χϡʔτϥϧঢ়ଶͷσʔλ͚ͩอଘͰ͖Δ ⾣ த్ͳঢ়ଶΛอଘͰ͖ͳ͍ঢ়گΛ࡞ΕΔ class Controller { var environment: Environment<Neutral> }
ঢ়ଶͱͯ͠อ͍࣋ͨ͠߹
χϡʔτϥϧ࣌ɺಡऔઐ༻ʹͯ͠ʜ
⾣ σʔλɺجຊঢ়ଶͰಡΈऔΓػೳ͚ͩΛඋ͑Δ
⾣ ฤूঢ়ଶΛऔಘ͢ΔϝιουΛඋ͑Δ
struct Environment<State> {
fileprivate(set) var platform: Platform
fileprivate(set) var version: Double
func startEditing() -> Environment<Editing> {
return Environment<Editing>(
platform: platform, version: version)
}
}
ঢ়ଶͱͯ͠อ͍࣋ͨ͠߹
ฤू࣌ʹɺॻ͖ػ͑ೳΛఏ͢ڙΔͱʜ
⾣ &EJUJOHঢ়ଶͰɺฤूՄೳͳϝιουΛ࣋ͭ
⾣ χϡʔτϥϧঢ়ଶΛऔಘ͢ΔϝιουΛඋ͑Δ
extension Environment where State: Editing {
mutating func set(platform: Platform) { … }
mutating func set(version: Double) { … }
func endEditing() -> Environment<Neutral> {
return Environment<Neutral>(
platform: platform, version: version)
}
}
ঢ়ଶͱͯ͠อ͍࣋ͨ͠߹ ϩʔΧϧείʔϓͰͷฤूΛอূͰ͖Δ ⾣ ฤू࣌ʹऔΓग़͠ɺฤू͖ॻʹޙ͕ٛ͢ੜ·ΕΔ ⾣ ૢ࡞Ͱ͖ͳ͍ͨΊɺฤूதͷࢀরΛ͛Δ func update() { // 編集状態で取り出さないと、書き込めない var environment = self.environment.startEditing() environment.set(platform: .macOS) environment.set(version: Platform.macOS.latest) // ローカルで編集を終了したら、書き戻す self.environment = environment.endEditing() }
Πϯελϯεͷ͕ؔ͠ڮ৺Ͳ͜Ζ
Πϯελϯεͷ͕ؔ͠ڮ৺Ͳ͜Ζ
Πϯελϯεͷৼସ͕ඞཁ
⾣ 1IBOUPN5ZQFɺҧ͏ΠϯελϯεΛ࡞Γ͢
⾣ ঢ়ଶมߋ࣌ʹɺ༰ΛҾ͖͙ܧඞཁ͕͋Δ
extension Environment where State: Editing {
var platform: Platform
var version: Double
// 同じ内容の、別インスタンスを作り直している
func endEditing() -> Environment<Neutral> {
return Environment<Neutral>(
platform: platform, version: version)
}
Πϯελϯεͷ͕ؔ͠ڮ৺Ͳ͜Ζ
༰ҾܧΛɺ؆୯ʹͰ͖ͳ͍ͩΖ͏͔ʁ
⾣ ෳ͢ΔॲཧΛશͯॻ͘ͷɺखؒͱ࿙Ε͕৺
⾣ ϓϩύςΟʔΛ૿ͨ࣌͠ͷɺमਖ਼Εෆ҆
extension Environment where State: Editing {
// 内容を原始的にコピーしないといけないとき
func endEditing() -> Environment<Neutral> {
var result = Environment<Neutral>()
result.platform = platform
result.version = version
return result
}
Πϯελϯεͷ͕ؔ͠ڮ৺Ͳ͜Ζ มΠχγϟϥΠβʔͰҾ͖ํ͙ܧ๏
Πϯελϯεͷ͕ؔ͠ڮ৺Ͳ͜Ζ มΠχγϟϥΠβʔͰҾ͖͙ܧ ⾣ มΠχγϟϥΠβʔ4XJGUͷ׳श ⾣ มͷΛɺ࠷͍ۙ̍͠ՕॴʹूͰ͖Δ struct Environment<State> { fileprivate(set) var platform: Platform fileprivate(set) var version: Double } extension Environment { fileprivate init<S>(takeover: Environment<S>) { platform = takeover.platform version = takeover.version } }
Πϯελϯεͷ͕ؔ͠ڮ৺Ͳ͜Ζ
؆୯ͳखଓ͖Ͱɺঢ়ଶมߋ͕Մೳʹ
⾣ มΠχγϟϥΠβʔʹTFMGΛ͚ͩ͢
⾣ Ͳͷঢ়ଶ͔Βɺಉ͡ํ๏ͰมՄೳ
extension Environment where State: Editing {
func endEditing() -> Environment<Neutral> {
return Environment<Neutral>(takeover: self)
}
}
ϓϩύςΟʔՃ࣌ΠχγϟϥΠβʔͷௐ͚ͩͰ0,
Πϯελϯεͷ͕ؔ͠ڮ৺Ͳ͜Ζ ෦ίϯςΩετͰҾ͖ํ͙ܧ๏
Πϯελϯεͷ͕ؔ͠ڮ৺Ͳ͜Ζ ෦σʔλΛߏମʹ·ͱΊΔ ⾣ ෦σʔλΛͻͱͭͷߏମͰද͢ݱΔ ⾣ Ҿ͖͗ܧɺߏମͷίϐʔੑʹͤΔ struct Environment<State> { // データをここで集中管理する fileprivate struct Context { var platform: Platform var version: Double } // これだけを引き継げば済む状況を作る fileprivate var _context: Context }
Πϯελϯεͷ͕ؔ͠ڮ৺Ͳ͜Ζ ෦ίϯςΩετͰҾ͖͙ܧ ⾣ ίϯςΩετΛҾ͖͙ܧΠχγϟϥΠβʔΛ࡞Δ ⾣ ίϯςΩετͷ༰͕૿͑ͯɺෳ͠࿙Εͳ͍ extension Environment { fileprivate init(context: Context) { _context = context } }
Πϯελϯεͷ͕ؔ͠ڮ৺Ͳ͜Ζ ؆୯ͳखଓ͖Ͱɺঢ়ଶมߋ͕Մೳʹ ⾣ มΠχγϟϥΠβʔʹίϯςΩετΛ͚ͩ͢ ⾣ ҙͷίϯςΩετͷࠩ͠ସ͑Մೳ extension Environment where State: Editing { func endEditing() -> Environment<Neutral> { return Environment<Neutral>(context: _context) } } ϓϩύςΟʔΛՃͯ͠ɺશ͕ͯࣗಈͰෳ͞ΕΔ
Πϯελϯεͷ͕ؔ͠ڮ৺Ͳ͜Ζ ίϯςΩετ͔ΒΛͱΔػೳ͕ඞཁ ⾣ ܕࢉܭϓϩύςΟʔͰɺίϯςΩετΛհ͢Δ ⾣ Εͯɺ෦σʔλͷҾ͖͗ܧΕΑΓ҆શʁ extension Environment { var platform: Platform { get { _context.platform } set { _context.platform = newValue } } var version: Double { get { _context.version } set { _context.version = newValue } }
Πϯελϯεͷ͕ؔ͠ڮ৺Ͳ͜Ζ ४උθϩͰҾ͖ํ͙ܧ๏
Πϯελϯεͷ͕ؔ͠ڮ৺Ͳ͜Ζ
ߏମΛɺܕ੍ڧมͰҾ͖͛ܧΔ
⾣ શͯͷঢ়ଶͰɺಉ͡ߏ͕ଋ͞Ε͍ͯΔ
⾣ VOTBGF#JU$BTUͰɺܕใΛࠩ͠ସ͑Δ
extension Environment where State: Editing {
func endEditing() -> Environment<Neutral> {
// 準備不要で、いきなりビットキャスト可能
return unsafeBitCast(self,
to: Environment<Neutral>.self)
}
}
શͯͷ෦σʔλ͕ɺ·Δ͝ͱෳ͞ΕΔ
Πϯελϯεͷ͕ؔ͠ڮ৺Ͳ͜Ζ
ߏମΛɺܕ੍ڧมͰҾ͖͛ܧΔ
⾣ มΠχγϟϥΠβʔͰɺܕ੍ڧมͰྑ͍
⾣ ͪ͜Βಉ༷ɺશͯͷσʔλΛશʹҾ͖͛ܧΔ
extension Environment {
fileprivate init<S>(takeover: Environment<S>) {
self = unsafeBitCast(takeover,
to: Environment.self)
}
}
1IBOUPN5ZQFͳΒͰͷɺܕͷಉҰੑ͕͖׆Δ໘
Πϯελϯεͷ͕ؔ͠ڮ৺Ͳ͜Ζ ΫϥεΛܕ੍ڧม͢Δ߹ͷҙ ⾣ ϝλใɺݩͷܕύϥϝʔλʔΛҡ࣋͢Δ ⾣ ࣮ࡍͷΠϯελϯεҎ্ͷμϯΩϟετݥة let sub: Base = Sub() let base: Base = Base() // 実体が Sub なので、全ての機能が使える let obj: Sub = unsafeBitCast(sub, to: Sub.self) // 実体が Base なので、Sub の機能を使うとクラッシュする let obj: Sub = unsafeBitCast(base, to: Sub.self)
࡞ઓΛ࿅Δ
ঢ়ଶΛ֦ுͰ͖Δʁ
ঢ়ଶΛ֦ுͰ͖Δʁ ઃ࣍ܭୈͰɺ͔ޙΒঢ়ଶՃՄೳ ⾣ ֦ܕுͰɺ࠷ॳ͔Β͋ͬͨΈ͍ͨʹՃͰ͖Δ ⾣ ͦΕΛݟӽͨ͠ࣄલઃܭඞཁ class Test: OperationState {} extension HogeOperation where State: Test { func testSomething() {…} } extension HogeOperation where State: Init { func testing() -> HogeOperation<Test> {…} }
ঢ়ଶͷɺαϒλΠϓʜʁ
ঢ়ଶͷɺαϒλΠϓʜʁ ঢ়ଶɺܧঝͰ͖Δ ⾣ ঢ়ଶΛɺΫϥεܧঝͰද͖ͰݱΔ ⾣ ܧঝ͋ʹݩΔɺͯ͢ͷػೳΛҾ͖͛ܧΔ // Test 状態を Prepared から継承させれば… class Test: Prepared {} // Test には Prepared の機能も備わる extension HogeOperation where State: Prepared { func execute() {…} } extension HogeOperation where State: Test { func testSomething() {…} }
ͻͱ·ͣɺఁऴྃ
ͱʹ͔͑͘ݴΔ͜ͱ 1IBOUPN5ZQFʹग़⁊͓͔͑ͨ͛Ͱ ϏϧυλΠϜͰͷ࣮ߦ੍ʹޚҙ͕ࣝ͘Α͏ʹͳͬͨ ҆ܕશͷͻͱͭͷදݱख๏ͱͯ͠༗༻
͡Ίͯ1IBOUPNͱૺ۰ͯ͠ ҋӢʹಆ͍ΛΜͰΈͨ ͜ΕͰ1IBOUPNͱಆ͑ͦ͏ʁ Πϥετग़లIUUQقઅͷΠϕϯτDPN
&OKPZ4XJGU 5IBOLZPV ͡Ίͯ1IBOUPNͱૺ۰ͯ͠ ҋӢʹಆ͍ΛΜͰΈͨ &;/&5۽୩༑ IUUQF[OFUKQ ⾣ 1IBOUPN5ZQF ⾣ ܕͷফ໓ੑɺସදݱΛఁ ⾣ ܕͷಉҰੑͱΠϯελϯεͷৼସ ⾣ ࣄޙͷ֦ுͱαϒλΠϓ