デコレータ(修飾子)

badge

デコレータは型や関数に特定の状態や振る舞いを追加したり明示するために使われます。 デコレータの文法は以下の通りです。

@deco
X = ...

デコレータは、競合しない限り複数つけることができます。

デコレータは特別なオブジェクトではなく、その実体は単なる1引数関数です。デコレータは以下の疑似コードと等価です。

X = ...
X = deco(X)

Ergでは変数の再代入が出来ないので、上のようなコードは通りません。 単なる変数の場合はX = deco(...)と同じなのですが、インスタントブロックやサブルーチンの場合はそうすることができないので、デコレータが必要になってきます。

@deco
f x =
    y = ...
    x + y

# コードが横長になるのを防ぐこともできる
@LongNameDeco1
@LongNameDeco2
C = Class ...

以下に、頻出の組み込みデコレータを紹介します。

Inheritable

定義する型が継承可能クラスであることを示します。引数scope"public"を指定すると、外部モジュールのクラスでも継承できるようになります。デフォルトでは"private"になっており、外部からは継承できません。

Final

メソッドをオーバーライド不能にします。クラスに付けると継承不能クラスになりますが、デフォルトなので意味はありません。

Override

属性をオーバーライドする際に使用します。Ergではデフォルトで基底クラスと同じ属性を定義しようとするとエラーになります。

Impl

引数のトレイトを実装することを示します。

Add = Trait {
    .`_+_` = Self.(Self) -> Self
}
Sub = Trait {
    .`_-_` = Self.(Self) -> Self
}

C = Class({i = Int}, Impl := Add and Sub)
C.
    @Impl Add
    `_+_` self, other = C.new {i = self::i + other::i}
    @Impl Sub
    `_-_` self, other = C.new {i = self::i - other::}

Attach

トレイトにデフォルトで付属するアタッチメントパッチを指定します。 これによって、Rustのトレイトと同じ挙動を再現できます。

# foo.er
Add R = Trait {
    .AddO = Type
    .`_+_` = Self.(R) -> Self.AddO
}
@Attach AddForInt, AddForOdd
ClosedAdd = Subsume Add(Self)

AddForInt = Patch(Int, Impl := ClosedAdd)
AddForInt.AddO = Int
AddForOdd = Patch(Odd, Impl := ClosedAdd)
AddForOdd.AddO = Even

こうすると、他のモジュールからトレイトをインポートした際に、アタッチメントパッチが自動で適用されます。

# 本来IntIsBinAdd, OddIsBinAddも同時にインポートする必要があるが、アタッチメントパッチなら省略できる
{BinAdd;} = import "foo"

assert Int.AddO == Int
assert Odd.AddO == Even

内部的にはトレイトの.attachメソッドを使って結びつけているだけです。コンフリクトする場合はトレイトの.detachメソッドで外すことができます。

@Attach X
T = Trait ...
assert X in T.attaches
U = T.detach(X).attach(Y)
assert X not in U.attaches
assert Y in U.attaches

Deprecated

変数の仕様が古く非推奨であることを示します。

Test

テスト用サブルーチンであることを示します。テスト用サブルーチンはerg testコマンドで実行されます。