LoginSignup
0
0

moduleとclass

Last updated at Posted at 2023-12-06

UWSCではclass/module構文でモジュールを定義することができました。class構文もmodule構文も機能としては同一で、機能的にはモジュールなのだからじゃあclassってなんだったの?という感じですがこれは歴史的経緯でこうなっています。
UWSCRではどうせならclassをもう少しそれっぽくしてみたいということで、仕様を大幅に変更しました。moduleはほぼそのままです。

moduleの変更点

module構文はUWSCのそれとほぼ同等ですが、以下の変更点があります。

どこにでも書ける

ユーザー定義関数と同様に、スクリプト下部に書かなければいけないという制限は撤廃されています。

globalの拡張

特殊変数globalが拡張されています。UWSCではglobal.関数名()とすることでグローバル関数を明示的に呼び出すことができましたが、UWSCRでは変数や定数も呼び出せるようになっています。

const Hoge = "global"

module MyModule
    const Hoge = "module"
    
    function module_hoge()
        result = Hoge
    fend
    function global_hoge()
        result = global.Hoge
    fend
endmodule

print MyModule.module_hoge() // module
print MyModule.global_hoge() // global

プライベート関数の定義

UWSCではプライベート関数を定義することができませんでしたが、UWSCRでは無名関数を使うことでプライベート関数を実現できます。

module MyModule
    dim private = function()
        result = "private"
    fend
    
    function call_private()
        result = private()
    fend
endmodule

print MyModule.call_private() // private
print MyModule.private()      // エラーになる(※)

なんと執筆時点のバージョン0.14.0だとプライベート関数がエラーにならないというバグがありました!上記コードの※部分はprivateと出力されてしまいます。なんてこった…。
0.15.0までには修正します…!

classの変更点

class構文の記述方法には特に変更はありませんが、機能的には一切互換性がありません。moduleのような呼び出しが一切できなくなっていますが、代わりにクラス名()とすることでクラスのインスタンスを得られるようになっています。

class定義

classを定義する場合はコンストラクタ(クラス名と同名の関数)が必須です。

class MyClass
    dim name

    // コンストラクタ (必須)
    procedure MyClass(name: string)
        this.name = name
    fend
endclass

moduleのコンストラクタはスクリプト開始時 (厳密にはmodule評価時) に実行されていましたが、classのコンストラクタはインスタンス作成時に実行されます。

インスタンスの作成

クラス名()というようにクラスを関数のように呼び出すとクラスインスタンスが返ります。このときコンストラクタが呼び出されます。

class MyClass
    dim name

    // コンストラクタ
    // 文字列を受けて自身のnameに代入する
    procedure MyClass(name: string)
        this.name = name
    fend

    function name()
        result = this.name
    fend
endclass

// コンストラクタに"stuncloud"を渡す
// 戻り値がクラスインスタンスになる
me = MyClass("stuncloud")
// メソッドを呼ぶ
print me.name() // stuncloud

デストラクタ

_class名_()という名前の関数を定義するとデストラクタになります。デストラクタは引数を取りません。
デストラクタは原則インスタンスへの参照が全て失われた場合に実行されます。それ以外にも以下の状況で実行されます。

  • インスタンス変数にNOTHINGを代入した場合 (この場合インスタンスのコピーがあっても全て破棄される)
  • with式でコンストラクタを呼んだ場合でendwithに到達したとき
  • スコープを抜ける際に、すべてのインスタンスがそのスコープのローカル変数だった場合
  • スクリプト終了時
class MyClass
    dim name

    // コンストラクタ
    procedure MyClass(name: string)
        this.name = name
    fend

    // デストラクタ
    procedure _MyClass_()
        print "good bye, <#name>"
    fend

    function name()
        result = this.name
    fend

endclass

hoge = MyClass("hoge")
// インスタンス変数に別の値を代入
// すべての参照が失われるのでデストラクタが実行される
hoge = "" // good by, hoge

fuga = MyClass("fuga")
// インスタンスのコピーを作っておく
fuga2 = fuga

// NOTHINGを代入
// コピーも含めてインスタンスを破棄するためデストラクタが実行される
fuga = NOTHING // good by, fuga
print fuga2 // NOTHING

// with式がコンストラクタならendwithで破棄される
with MyClass("piyo")
    print .name() // piyo
endwith // good bye, piyo

…と書いたもののなんと執筆時点のバージョン0.14.0ではデストラクタが動作していませんでした!さらにNOTHINGを代入してしまうとデッドロックします!ヤバイ!
これも0.15.0までに修正します。

0.14.0で発生している数々のバグについて

バージョン0.12.1でmodule/classに関するバグを修正した際に、実はかなり大掛かりな変更が行われていました。そこで様々な不具合を含んでしまっていたのですが、その後の検証やテストが不十分であったためそれらが検知できていませんでした。このアドベントカレンダーの記事を書くに当たって記事内のコードは必ず動作確認をするのですが、そこで初めてこれらのバグが表面化しました。
これらは必ず次回リリース予定の0.15.0で修正します。この記事を読んでくださっている方にはご迷惑をおかけしますが、0.14.0でのサンプルコードの実行はお控えいただくようお願いいたします。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0