🕌

Verse言語の設計思想を読み解きたい(15) 型システム①クラスの初期化

2023/04/22に公開

前回はこちら。
https://zenn.dev/t_tutiya/articles/0ed2f969a267ad

今回はクラスの初期化周りについて(前回予告してた非同期の続きは色々あって順延)、公式ドキュメント上で散らばっている情報をまとめました(クラスの詳細は公式ドキュメントを参照してください)。

クラス定義

https://dev.epicgames.com/documentation/ja-jp/uefn/class-in-verse

クラス(class)」の定義にはclass式を使います。calss式のコードブロックに変数(定数)と関数の定義を記述します。

cat := class:
    Name : string
    var Age : int = 0
    Sound : string

    Meow() : void = DisplayMessage(Sound)

前回説明した通り、コードブロックは他の書式と可換です。

cat := class{ Name : string;var Age : int = 0;Sound : string;Meow() : void = Print(Sound)}

クラスに属する変数(定数)は「フィールド(Field)」、関数は「メソッド(Method)」と呼びます。フィールド/メソッドを合わせて「クラスメンバ(class member)」あるいはメンバと呼びます。
変数(定数)の定義と異なり、メンバフィールドの定義ではデフォルト値を省略できます。ただし、省略した場合はインスタンスの定義時に値を設定する必要があります。

ちなみに、クラスの継承は以下の様になります(catクラスを継承してblackCatクラスを定義する場合)。

blackCat := class(cat):
    #サブクラスの変数/メソッド定義

クラスインスタンスの定義

定義したクラスを使用するには、元となるクラス情報から「クラスインスタンス(class instance)」を定義する必要があります。インスタンスを定義する事を「インスタンス化(instantiation)」とも言います。

インスタンスは「オブジェクト(object)」とも呼ばれます(Verseでは両者を区別しません)。

Verseではクラスからインスタンスを定義する方法として、「アーキタイプ」を使う方法と「コンストラクタ」を使う方法の2種類があります。

アーキタイプ(archetype)

アーキタイプ(archetype)」はクラスメンバの初期化のみを行うインスタンス化の方法で、C#のオブジェクト初期化子に似ています。

以下は、アーキタイプによるインスタンス化[1]のサンプルコードです。

OldCat := cat{Name := ”Percy”, Age := 20, Sound:= ”Rrrr”}

先程のcatクラスをインスタンス化してOldCatとして定義しています。クラス名(ここでは"cat")の後のブレス括弧の部分がアーキタイプです[2]

アーキタイプでは、クラスに定義されている全てのフィールドの値を定義しなければいけません。ただし、クラス定義時にデフォルト値が設定されている場合は省略できます(その場合はデフォルト値が使用されます)。

また、アーキタイプはコードブロックなので、クラス定義と同じく他の書式と可換です。

OldCat := cat:
	Name := "Percy"
	Age := 20
	Sound:= "Rrrr"

ちなみに、「構造体(struct)」も同じようにアーキタイプを使ってインスタンス化します。

コンストラクタ(Constructor)

https://dev.epicgames.com/documentation/ja-jp/uefn/constructor-in-verse

コンストラクタ(Constructor)」はクラスをインスタンス化する際になんらかの処理を実行できる方法で、C#のコンストラクタメソッドに相当します。

Verseのコンストラクタは、クラスに関連づけられた特殊な関数です。Verseのコンストラクタ関数はC#と違ってクラスメンバではありません。

コンストラクタ関数は常にモジュール関数(ファイルのトップレベルスコープに記述される関数の事)になります。

以下はコンストラクタによるインスタンス化のサンプルコードです。

BoneNoisyCat<constructor>(name : string, sound: string) := cat:
    Name := name
    Sound:= sound + " " + sound + " " + sound

このコンストラクト関数を使ったインスタンス化は以下になります。

myCat := BoneMikeCat("NoName","Myao")

※v26.20現在、let式は廃止されたようです。またblock式も挙動が微妙です
let式とblock式を使うと、初期化の途中で処理を行う事ができます。
以下はlet式とblock式を使った初期化のサンプルコードです(上手い例を思いつかなかったので、あまり意味のある挙動はしていません)。

BoneMikeCat<constructor>(name : string) := cat:
    let:
        MikeSign := "Mike"
        var TruthAge :int = 0

    Name := MikeSign + name + MikeSign

    block: 
        set TruthAge += 999

    Age := TruthAge

    Sound:= "MikeMike"

let式のコードブロックでは、コンストラクタ内だけで使用する変数(定数)を定義できます(変数定義以外の式を記述するとコンパイルエラー)[3]
block式のコードブロックでは、コンストラクタ関数の引数やlet式で定義した変数を使って演算が出来ます(逆に、変数定義が出来ません)。

型変換(type casting)

Verseでは「親クラス(super class)」から「子クラス(sub class)」にダウンキャストする事を「型変換(type casting)」と呼びます。

型変換の書式は以下になります。

NewReference := type_to_cast_to[Reference]

Referenceが元のオブジェクト、type_to_cast_toが変換したい子クラスの型になります。

型変換は失敗許容式です。型変換に失敗した場合は値が返らず、その為失敗コンテキスト内でしか記述できません。型変換自体の記法も、配列アクセスなど他の失敗許容式に揃えているようです。

余談:float型からint型への変換

ちなみに、froat型からint型への変換には型変換は使えません。これは、float型とint型には直接の親子関係が無いためです。

その代わり、組み込みの関数Int[]が提供されているので、以下のように変換出来ます(ただしInt[]は失敗許容関数なので、実際には失敗コンテキスト内でしか記述できません)。

arg_float : float = 1.0
arg_int : int = Int[arg_float]

int型からfloat型への変換のための関数は用意されていませんが、以下のようにすれば変換できます。

arg_int : int = 1
arg_float : float = 1.0 * arg_int

他の関連項目

Verseでは、クラスは「複合型(Composite Types)」というカテゴリに属します。
https://dev.epicgames.com/documentation/ja-jp/uefn/composite-types-in-verse
以下の記事も複合型に含まれます。参考にどうぞ。
列挙型(Enum)
https://dev.epicgames.com/documentation/ja-jp/uefn/enum-in-verse
サブクラス(Subclass)
https://dev.epicgames.com/documentation/ja-jp/uefn/subclass-in-verse
構造体(Struct)
https://dev.epicgames.com/documentation/ja-jp/uefn/struct-in-verse
インターフェイス(Interface)
https://dev.epicgames.com/documentation/ja-jp/uefn/interface-in-verse

#Fortnite #Verse #VerseLang #UEFN

続き

https://zenn.dev/t_tutiya/articles/a762bb727e255f

脚注
  1. 公式訳では「アーキタイプインスタンス化(archetype instantiation)」 ↩︎

  2. 公式ドキュメントでは表記が揺れていて、「パラメータが全て初期化されたインスタンス」もアーキタイプと呼ぶ場合があります。 ↩︎

  3. 調べた限りでは、let式はコンストラクタ内でのみ有効のようです ↩︎

Discussion