概要

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」のような実装もすることができるのです。