概要
sample := class<castable>:
クラスに対するエフェクト指定子として「castable」という機能が用意されています。
sample := class<castable>:
sample_child_a := class(sample):
sample_child_a_b := class(sample_child_a):
sample_child_b := class(sample):
Filter(InCastable:castable_subtype(sample)):[]InCastable=
Samples := array:
sample{}
sample_child_a{}
sample_child_a_b{}
sample_child_b{}
Result :=
for:
Element : Samples
CastedElement := InCastable[Element]
do:
CastedElement
return Result
Function():void=
Result := Filter(sample) # sample, sample_child_a, sample_child_bが配列で返ってくる
Result := Filter(sample_child_a) # sample_child_a, sample_child_a_bが配列で返ってくる
Result := Filter(sample_child_b) # sample_child_bが配列で返ってくる
castableは関数に対する「castable_subtype」と一緒に使用されます。引数として渡してあげることで、そのcastable_subtypeの入った変数を用いてキャストの確認を行うことができます。
通常のsubtypeとの違い
sample := class:
Do():void=
Print("Called Do")
sample_child_a := class(sample):
sample_child_a_b := class(sample_child_a):
sample_child_b := class(sample):
CallDo(InSubtype:subtype(sample)):void=
InSubtype.Do()
Function():void=
InstancedSample := sample{}
InstancedSampleChildA := sample_child_a {}
CallDo(InstancedSample) # Called Doと表示される
CallDo(InstancedSampleChildA) # Called Doと表示される
Verse言語ではcastableのつかない通常の「subtype」型も用意されています。castable付きとの違いとして、通常のモノは関数に対して「クラスのインスタンス」を渡す仕様となっています。
Filter(InSubtype:subtype(sample)):[]InCastable=
Samples := array:
sample{}
sample_child_a{}
sample_child_a_b{}
sample_child_b{}
Result :=
for:
Element : Samples
CastedElement := InSubtype[Element] #インスタンス自体が渡されるのでキャストに使えない
do:
CastedElement
return Result
そのため、forやif内でのキャストは行えません。
sample := class:
Do():void=
Print("Called Do")
sample_child_a := class(sample):
sample_child_a_b := class(sample_child_a):
sample_child_b := class(sample):
CallDo(InCastable:castable_subtype(sample)):void=
InCastable.Do() #型自体が渡っているため関数や変数にはアクセスできない
また、castable_subtypeではインスタンスではなく「クラスの型」を渡しているため、引数に対して特定の関数や変数を呼び出すということはできません。castable_subtypeは、キャストの確認専用です。
仕様まとめ
1. 指定子はクラス/インターフェースに付与できる
sample_interface := interface<castable>:
sample := class(sample_interface):
castableはクラスまたはインターフェースに付与できるエフェクト指定子です。付与された親を継承した子クラスは、すべてcastableを引き継ぎます。
2. castable_subtypeとして使用できる
sample_interface := interface<castable>:
sample := class(sample_interface):
sample_child_a := class(sample):
sample_child_a_b := class(sample_child_a):
sample_child_b := class(sample):
sample_child_b_c := class(sample_child_b):
Filter(InSample:castable_subtype(sample_interface)):[]InSample=
Array := array:
sample{}
sample_child_a{}
sample_child_a_b{}
sample_child_b{}
sample_child_b_c{}
for:
Element : Array
CastedElement := InSample[Element]
do:
CastedElement
Function():void=
Result := Filter(sample_child_a)
Result := Filter(sample_child_a{}) #インスタンスのためエラー
castableが付与されたクラスは「castable_subtype」として使用できます。引数としてクラスを型として受け取ることができ、主にifやforに対するキャストで使用できます。
関数を呼び出す際は「{}波カッコ」をつけずにカッコ内に渡します。
2-b. キャスト専用
Filter(InSample:castable_subtype(sample_interface)):[]InSample=
InstancedSample := InSample{} #エラー
castable_subtypeはキャスト専用のため、関数内でインスタンス化するために使用することはできません。
2-c. castableをつけないと
Result := Filter(sample_child_a)
This function parameter expects a value of type castable_subtype(sample), but this argument is a non-castable value of type type(sample_child_a, sample_child_a).
castableをつけないクラスに対してcastable_subtypeを試みると、上記のようなエラーが発生します。
3. 型が入るため戻り値としても使用できる
sample_interface := interface<castable>:
sample := class(sample_interface):
sample_child_a := class(sample):
sample_child_a_b := class(sample_child_a):
sample_child_b := class(sample):
sample_child_b_c := class(sample_child_b):
Filter(InSample:castable_subtype(sample_interface)):[]InSample=
Array := array:
sample{}
sample_child_a{}
sample_child_a_b{}
sample_child_b{}
sample_child_b_c{}
for:
Element : Array
CastedElement := InSample[Element]
do:
CastedElement
Function():void=
Result:[]sample_child_a = Filter(sample_child_a) #sample_child_a型の配列が返ってくる
Result:[]sample_child_b = Filter(sample_child_b) #sample_child_b型の配列が返ってくる
関数の戻り値として使用すると、関数の結果は呼び出し時点で渡した型と一致させることができます。
おまけ
変数としてsubtype
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
sample := class:
sample_child_a := class(sample):
sample_child_a_b := class(sample_child_a):
sample_child_b := class(sample):
sample_child_b_c := class(sample_child_b):
sample_device := class(creative_device):
@editable
EditableSubtype:subtype(sample) = sample_child_a
var VariableSubtype:subtype(sample) = sample_child_b
var VaritableCastable:castable_subtype(sample) = sample_child_b
FilterA():[]sample=
Array := array:
sample{}
sample_child_a{}
sample_child_a_b{}
sample_child_b{}
sample_child_b_c{}
for:
Element : Array
CastedElement := EditableSubtype[Element]
do:
CastedElement
FilterB():[]sample =
Array := array:
sample{}
sample_child_a{}
sample_child_a_b{}
sample_child_b{}
sample_child_b_c{}
for:
Element : Array
CastedElement := VariableSubtype[Element]
do:
CastedElement
実は、引数ではなく変数としてsubtypeを使用する場合、castable_subtypeと同じものとして使うことができます。例えばeditableにして公開したり、グローバル変数として定義したりできます。
castable_subtypeは、あくまで引数として使用する際に「インスタンス化されたもの」か「型自体」かを区別させるために追加されたとみて大丈夫です。
FilterA():[]EditableSubtype=
Array := array:
sample{}
sample_child_a{}
sample_child_a_b{}
sample_child_b{}
sample_child_b_c{}
for:
Element : Array
CastedElement := EditableSubtype[Element]
do:
CastedElement
ただし、戻り値の型をグローバル変数としてのsubtypeを使用することができません。
This function returns a value of type []false, but the function body’s result is an incompatible value of type []sample.
のようなエラーが出てしまうため、戻り値はそのクラスの親など型として使用する必要があります。汎用的な関数を作るのであれば、引数に対するcastable_subtypeの方が便利な場合も多いですが…ケースバイケースで考えていきましょう。
シーングラフでも使用されている
component<native><public> := class<abstract><unique><castable><final_super_base>:
実は、シーングラフの機能の一つでもある「component」はcastableを用いて定義されています。そのため、
GetComponent<native><final><public>(component_type:castable_subtype(component))<reads><decides>:component_type
のように、呼び出し時点で型を指定する「GetComponent」のような実装もすることができるのです。