LoginSignup
3
0

[WIP]ErgでPythonの資産が少し使えるようになった

Last updated at Posted at 2023-06-07

はじめに

Erg言語で一部の外部ライブラリが使えるようになった

具体的には以下の通りである

  • matplotlib
  • numpy
  • pandas
  • requests
  • setuptools
  • tqdm
  • urllib3

完全には型付け完了していないため、一部の機能には限定されてはいるがこれらライブラリを使えるようになっている

例えばNumpyだと:point_right:このように型付けがされている

# TODO: dependent (static shaped)
.NDArray = 'ndarray': (T: Type) -> ClassType
.NDArray(T) <: Output T
.NDArray(_) <: Num
.NDArray.
    shape: [Nat; _]
    ndim: Nat
    dtype: Type
    size: Nat

.abs: |T|(object: .NDArray(T),) -> .NDArray(T)
.add: |T|(object: .NDArray(T), other: .NDArray(T)) -> .NDArray(T)
.all: |T <: Num|(object: .NDArray(T),) -> Bool
.any: |T <: Num|(object: .NDArray(T),) -> Bool
.arange: |T <: Num|(start: T, stop := T, step := T) -> .NDArray(T)
.array: |T|(object: Iterable(T),) -> .NDArray(T)
.linspace: |T <: Num|(start: T, stop: T, num := Nat, endpoint := Bool, retstep := Bool, dtype := Type, axis := Nat) -> .NDArray(T)
.max: |T <: Num|(object: .NDArray(T),) -> T
.mean: |T <: Num|(object: .NDArray(T),) -> T
.min: |T <: Num|(object: .NDArray(T),) -> T
.ones: |T|(shape: Nat or [Nat; _], dtype := Type) -> .NDArray(T)
# タイポが現在2023/06/ddであるのでreshapceからreshapeに直す
.reshape: |T|(object: .NDArray(T), shape: [Nat; _]) -> .NDArray(T)
.std: |T <: Num|(object: .NDArray(T),) -> T
.sum: |T|(object: .NDArray(T),) -> T
.sqrt: |T|(object: .NDArray(T),) -> .NDArray(T)
.transpose: |T|(object: .NDArray(T), axes := [Nat; _]) -> .NDArray(T)

型付けに関する概要は:point_right:こことかを読めば大まかにはわかる

.d.erファイルを作成してPythonに対して型をつければ基本的にはPythonのライブラリ(モジュール)は使えるようになる

私自身いろいろとPRをためてしまっているので、この型付け作業に関してはまだできていないため、詳しい解説ができないがPandasscikit-learnの型付け作業はしたい

最近の主要ライブラリは型注釈をつけるのが一般的となってきている

そのため、Pylyzerを使い、型注釈を参考にすれば主要なライブラリの多くは型付けができる

:point_right: Pylyzer

$ pylyzer --dump-decl test.py
Start checking: test.py
All checks OK: test.py

$ ls
test.py  test.d.er

Numpyのinit.d.erに対して、reshapeだったりmaxメソッドをつけようとしたら以下のように書くだけで、できるようになる

# TODO: dependent (static shaped)
.NDArray = 'ndarray': (T: Type) -> ClassType
.NDArray(T) <: Output T
.NDArray(_) <: Num
.NDArray.
    shape: [Nat; _]
    ndim: Nat
    dtype: Type
    size: Nat
+   .reshape: |T|(shape: [Nat; _]) -> .NDArray(T)
+   .max: |T <: Num|() -> T

.abs: |T|(object: .NDArray(T),) -> .NDArray(T)
.add: |T|(object: .NDArray(T), other: .NDArray(T)) -> .NDArray(T)
.all: |T <: Num|(object: .NDArray(T),) -> Bool
.any: |T <: Num|(object: .NDArray(T),) -> Bool
.arange: |T <: Num|(start: T, stop := T, step := T) -> .NDArray(T)
.array: |T|(object: Iterable(T),) -> .NDArray(T)
.linspace: |T <: Num|(start: T, stop: T, num := Nat, endpoint := Bool, retstep := Bool, dtype := Type, axis := Nat) -> .NDArray(T)
.max: |T <: Num|(object: .NDArray(T),) -> T
.mean: |T <: Num|(object: .NDArray(T),) -> T
.min: |T <: Num|(object: .NDArray(T),) -> T
.ones: |T|(shape: Nat or [Nat; _], dtype := Type) -> .NDArray(T)
.reshape: |T|(object: .NDArray(T), shape: [Nat; _]) -> .NDArray(T)
.std: |T <: Num|(object: .NDArray(T),) -> T
.sum: |T|(object: .NDArray(T),) -> T
.sqrt: |T|(object: .NDArray(T),) -> .NDArray(T)
.transpose: |T|(object: .NDArray(T), axes := [Nat; _]) -> .NDArray(T)

このような.d.erファイルの作成や修正を作ってもらえれば既存のPython資産を既に扱える状況にある

型を持って推論もしてくれるLightなRust/RobustなPythonを書けるようになる

型付けが非常に大変な作業ではあるが、DjangoのようなWebフレームワークから、Pytorchのような機械学習ライブラリまで幅広くPythonの資産を使える前段階までは来た

もし気になった人がいれば、上記みたいなな小さいのでも良いのでライブラリの型付けをPRとして作ってもらえると嬉しい

特にこのような型付けや修正は割とgood first issueになるので、GithubでPRを作ったことがない人にもおすすめできる

Github上でもDiscord上でも良いので質問や話しをしながらながらでもIssueやPRを作ってくれると良い経験になると思う

可能なら型付けしたその道筋をドキュメントとして残してくれるとありがたい


今回はどのように資産が扱えるかを軽く紹介しながら、Ergのコードを眺めてもらうことを目的としている

環境構築

現状はまだ、Ergで環境構築する方法はない

一応パッケージマネージャーの計画はやパッケージシステムなどがある

ErgのコンパイラをダウンロードしてPythonの環境のみを構築すれば良い

Erg releaseから自分の環境に合わせてダウンロードする

Rustがある人はcargo install ergでもできる

クローンをしても良い

git clone https://github.com/erg-lang/erg.git
cd erg
cargo install --path . --features "full japanese" # --features以降はなくても良い

Pythonはとりあえずvenvを使う

以下のはWindowsでの環境構築なので、MacやLinuxの人は適度に変える

python -m venv erg_venv # 名前は適当なので注意
. .\Scripts\Activate.ps1
python -m pip install -U pip matplotlib numpy tqdm

コードを書く際にはVSCodeを使うことをお勧めする

els(LSP)が既にあるため、基本的な機能は揃っている

  • 型表示(2023/06/dd現在、かなり冗長なのでIssueを作っている。あると便利)
  • 定義移動、実装移動
  • 自動補完、自動補完候補の選択
  • etc

ただ、頻繁にクラッシュするためまだ安定性が足りない

また、開発が途中だがJpyter Notebook用のカーネルも既にあるため、Ergをnotebook上で使うこともできる

Windowsではネイティブでは使えないが、WSL上では動作することは確認できている

ライブラリ(モジュール)のインポート

Ergのスクリプトを読み込むときにはimportを使用し、Pythonのスクリプトを読み込むときにはpyimportを使用する

# import numpy as np
np = pyimport "numpy"

読み込んだ内容は全て変数に代入するため、asを使ったエイリアスのようなことをせずにそのまま変数名で名前を決める

np = pyimport "numpy"
a = np.array([1, 2, 3, 4.3])
print! a
# array([1. , 2. , 3. , 4.5])

他のメソッドもこのように使える

one_2d = np.ones([5, 5])
print! one_2d

ag = np.arange(15)
print! np.reshape(ag, [3, 5])
# メソッドのreshapeを使うには上記の`init.d.er`でメソッドの定義が必要
rh_one = np.arange(25).reshape([5, 5])
print! rh_one

ls = np.linspace(0, 1, 10)
print! ls

注意して欲しい点はErgでは変数のシャドーイングや再代入の原則的な禁止である

よく慣らしで同じ変数名(aとかbarr)を使って再代入してしまう

しかしこれはエラーとして検出されていしまうので注意が必要になる

また、built-inモジュールのtimeなどは既に実装済みなので、これとtqdmを使った場合はこのような感じになる

{sleep!;} = pyimport "time"
tqdm = pyimport "tqdm"
for! tqdm.Tqdm!(0..4, total:=100), _ =>
    sleep!(0.25)

スクリーンショット 2023-06-07 094929.png

{sleep!;}などの文法はレコード可視性に詳細が書かれている

データの読み込み

私はデータ分析でPythonを使うので、データの読み込みができないとほぼ仕事にはならない

将来的にはPandas(Polar)が使えるようになるので、ここの章は丸ごと省略できるがまだできないので書いておく

データは/data/data.csvに保存してある

1,2,3,4,5
6,7,8,9,10
11,12,13,14,15

まだ、strip()メソッドがないので、データに余計な空白や改行があると以下のコードは使えない

date型やfloat型に対応するにはもう一工夫が必要になる

data = with! open!("./data/data.csv", mode:="r", encoding:="utf_8"), f =>
    arr = ![]
    for! f.read!().split("\n"), line =>
        lines = ![]
        for! line.split(","), e =>
            lines.push!(e.to_int())
        arr.push!(lines)
    arr

print! data

単純な数値データだとこのようにできる

ここでのポイントとしてはwith!open!のプロシージャと、ブロックによるスコープの厳密さがある

基本的には副作用がある関数は全て!をつけたプロシージャとしてErgでは特別扱いされるようになっている

ただ、これのおかげで代入される値がその後にエラーとして検出されたりした場合には、ここの副作用が悪さをしているからエラーとして検出されているなど目算をつけやすい

次にスコープの厳密さに関しては以下のようなことがPythonだとできてしまう

>>> with open("./data/data.csv", 'r') as f:  
...     data = f.read()
...
>>> print(data)
1,2,3,4,5
6,7,8,9,10
11,12,13,14,15
>>> for i in range(10):
...     a = i
...
>>> a
9

ブロックがありそのなかでdataaのような変数を作っても、それをブロックの外でも使用できる

このようにスコープの外に変数が漏れ出るのは意図しない挙動を生む

Ergではこれを厳密にチェックし、ブロック内で変数の寿命(lifetime)が終わるようになっている

Rustで有名になった所有権もErgでも一応存在するので使用した変数は所有権が移動したりして使えなくなったりなど制限がある

データの可視化

先程の読み込んだデータはMatplotlibで可視化することができる

matplotlib = pyimport "matplotlib"
plot! = matplotlib.pyplot.plot!
show! = matplotlib.pyplot.show!

data = with! open!("./data/data.csv", mode:="r", encoding:="utf_8"), f =>
    arr = ![]
    for! f.read!().split("\n"), line =>
        lines = ![]
        for! line.split(","), e =>
            lines.push!(e.to_int())
        arr.push!(lines)
    arr

_ = plot!(data)
_ = matplotlib.pyplot.xlabel!("x label")
_ = matplotlib.pyplot.ylabel!("y label")
discard matplotlib.pyplot.title!("fig title here")
discard matplotlib.pyplot.legend!(["line00", "line01", "line02"])

show!()

Figure_1.png

恐らくだがplt = pyimport "matplotlib.pyplot"はできない

plt = matplotlib.pyplotもできない

なので、プロシージャを変数に代入して使っている

現状使えるのは以下の6つだけ

  • show!
  • plot!
  • xlabel!
  • ylabel!
  • title!
  • legend!

show!以外は返り値があるため、その返り値を使わないとwarningが出てしまう

なので、_(プレースホルダ)discardを使用する(どちらでも良いはず)

コンパイル時のwarningはまだ消せないはず

よくあるsin波は以下のように書ける

{sin;} = pyimport "math"
matplotlib = pyimport "matplotlib"

plot! = matplotlib.pyplot.plot!
show! = matplotlib.pyplot.show!

x = 0..10

y = ![]
for! x, i =>
    y.push! sin i

discard plot!(x, y)
show!()

Figure_2.png

x = np.arange(-5, 5, -0.1)x = 0..10の代わりに使いたがったが、まだndarrayがイテレートできなかった

終わりに

徐々にではあるがErgの基礎的な機能だけではなく、Pythonの資産も使えるようになってきた

後はひたすら型を付ける作業をするだけで基本的にライブラリは使えるようになる

まだ使える機能が少なかったり冗長な部分が多いが、すぐに書けるという点ではRustよりはるかに書きやすかった

環境構築に関しては現状はvenvでPythonの環境を作るだけでErgが認識してくれるので問題はなかった

外部のライブラリを使うという点ではまだ機能(型付け作業)が少ないのと、Ergでどのように書くのが良いのかの知見が足りなかったので上手く表現しきれていなかった

ただ、大まかなErgの書き方と、どのように資産を使うのかの概要は伝えられた思う

全体的に粗削りな部分が多いがこれから少しずつ改善されていくと思うし、私自身改善していくつもりである(興味を持ってくれたらIssueやPRを作ってください)

3
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
3
0