4.7 KiB
Pythonとの連携
Pythonへのexport
Ergスクリプトをコンパイルすると.pycファイルが生成されますが、これは単純にPythonのモジュールとして読み込むことができます。 ただし、Erg側で非公開に設定した変数はPythonからもアクセスできません。
# foo.er
.public = "this is a public variable"
private = "this is a private variable"
erg --compile foo.er
import foo
print(foo.public)
print(foo.private) # AttributeError:
Pythonからのimport
Pythonから取り込んだオブジェクトはデフォルトですべてObject
型になります。このままでは比較もできないので、型の絞り込みを行う必要があります。
標準ライブラリの型指定
Python標準ライブラリにあるAPIは、すべてErg開発チームにより予め型が指定されています。なので、pyimport
でそのまま呼び出すことが出来ます。
time = pyimport "time"
time.sleep! 1
ユーザースクリプトの型指定
Pythonスクリプトの型ヒント(type hint)をErgは関知しません。
Pythonのfoo
モジュールに型を付けるfoo.d.er
ファイルを作成します。
# foo.py
X = ...
def bar(x):
...
def baz():
...
class C:
...
# foo.d.er
.X: Int
.bar!: Int => Int
.foo! = baz!: () => Int # aliasing
.C!: Class
d.er
内では宣言と定義(エイリアシング)以外の構文は使えません。
Python側での識別子がErgでは有効な識別子ではない場合、シングルクォーテーション('
)で囲むことでエスケープできます。
オーバーロード
Pythonの型付けだけで使える特殊な型として、オーバーロード型があります。これは、複数の型を受け取ることができる型です。
f: (Int -> Str) and (Str -> Int)
オーバーロード型はサブルーチン型のintersection(and
)を取ることで宣言できます。or
ではないことに注意してください。
こうすると、引数の型によって戻り値の型が変わる関数を宣言できます。
f(1): Str
f("1"): Int
型判定は左から順に照合され、最初にマッチしたものが適用されます。
このような多相はアドホック多相と呼ばれ、型変数とトレイト境界を用いるErgの多相とは異なるものです。アドホック多相は一般的にはあまり推奨されませんが、Pythonのコードでは普遍的に使われているので、必要悪として存在します。
オーバーロード型の引数型は部分型関係にあっても良く、引数数が違っていいても良いですが、同じ型であってはいけません。すなわち、return type overloadingは許可されません。
# OK
f: (Nat -> Str) and (Int -> Int)
f: ((Int, Int) -> Str) and (Int -> Int)
# NG
f: (Int -> Str) and (Int -> Int)
トレイト実装宣言
クラスに対してトレイトの実装とトレイトメンバーの宣言を行う場合、以下のように記述します(numpy.NDListの型宣言より抜粋)。
.NDList = 'ndarray': (T: Type, Shape: [Nat; _]) -> ClassType
...
.NDList(T, S)|<: Add .NDList(T, S)|.
Output: {.NDList(T, S)}
__add__: (self: .NDList(T, S), other: .NDList(T, S)) -> .NDList(T, S)
注意点
現在のところ、Ergはこの型宣言の内容を無条件に信用します。すなわち、実際にはInt型の変数でもStr型として宣言する、副作用のあるサブルーチンでも関数として宣言する、などができてしまいます。
また、自明な型付けでも型宣言を省略できないのは面倒なので、PythonスクリプトをErgの型システムで静的に型解析するプロジェクトが進められています。