erg version 0.5.11
erg version 0.6.0
erg version 0.6.4 <- ここ
python version 3.11.0 推奨(他だとバグが多い)
個人的にずっと気になってちょくちょくドキュメントや実装のお手伝いをしている
新しい言語が作られていくのが見ていてとても楽しい
内部的にはこうなっているのかという仕組みを学べて楽しい
pythonに型を宣言して書けるという喜び
ergにはdiscordもあるのでぜひ参加してみてください
Summary
まだ開発途中なので、変更や未実装な部分がたくさんあります
上手く動作しなかったり、panicを起こす可能性があります
ergはpythonと互換性を持つ静的型付け言語
特徴をあげると以下の通り
- pythonとの互換性
- lightなRust言語
- 強力な型推論
- 型完全
- オブジェクト指向言語であり、関数型言語でもある
基礎はもう出来上がっているので簡単なスクリプトは書ける
Install
まだ、パッケージマネージャなどはなくコンパラのみになっている
Releaseからダウンロードできる
Rust製なのでcargoでインストールもできる
cargo install erg # 英語
cargo install erg --features japanese # 日本語
cargo install erg --features simplified_chinese # 簡体字
cargo install erg --features traditional_chinese # 繁体字
# 追加の設定
cargo install erg --features debug # コンパイラのデバッグ情報が出力される
cargo install erg --features unicode # エラーの文字に一部unicodeを使う(デフォルトでascii)
# これは将来的にビビットとユニバーサルカラーに分けるつもり
cargo install erg --features pretty # カラーリングがビビットな色になる(エラーの絵文字付き)
cargo install erg --features full # 上記の全部(言語は除く)
# crosstermを使った操作
# linux、macで矢印キーが使えなかったのを使えるようにした
cargo install erg --features repl-full
ここで一つ特徴をあげると多言語対応している点である
デフォルトで英語、--features
フラグを使うと、日本語や中国語にエラーメッセージを変更することがでる
初心者には日本語をお勧めする
誤字・脱字・誤訳っぽいところがあればIssueを作ってください(なんならPRを作ってください)
ソースからビルドする
git clone https://github.com/erg-lang/erg.git
cd erg
cargo install --path . # 英語
cargo install --path . --features japanese # 日本語化
# 複数のフラグを使うこともできる
cargo install --path . --features "japanese full"
Ergコンパイラの基本
erg
でコンパイラを使うことができる
erg # REPLの起動
erg <script> # スクリプトのコンパイルと実行
erg --compile <Script> # スクリプトのコンパイル.pycを作成
erg -h # ヘルプテキスト表示
erg -V # バージョン表示(-vじゃないので注意)
# Erg 0.6.4
ergには複数のコマンドがあるが、いくつかはまだ未実装なので注意
オプションや引数のパースもまだ誤ったのを入力するとpanicを起こすのでもう少し綺麗にしたいところ
きちんとexitするようにした
基本はREPLが使えるのでこれを使って説明をしていく
version0.6.4以降ではインデントが自動で追加されるようになった
インデントを示唆する構文があるとそれに応じて4スペースが自動で挿入され、空欄の改行で自動でディデントされるようになる
また、linuxやmacで矢印キーが使えなかったがfeatureフラグでrepl-full
を追加すると矢印キーによる移動や、履歴をたどる、ペースト、Ctrl-zで終了などの機能が使えるようになる
> erg
Starting the REPL server...
Connecting to the REPL server...
Erg interpreter 0.6.0 on x86_64/windows
>>> a =
... 1
... # 空欄の改行を入れるとディデントされる
>>>
とりあえずHello, worldをしてみる
main関数を実装したりする必要がなく、これだけで実行ができる
>>> print!("hello, world") # コメント
hello, world
コメントは#
で複数行コメントは#[
~]#
になる
!
は副作用のあるプロシージャであることを示している(詳細)
ergでは括弧に曖昧性がなければ省略して書くことができる(参照)
>>> print! 1, "a", True, [1, 2, 3]
1 a True [1, 2, 3]
end:="something"
引数を取って改行の代わりに別のを代入することもできる
ちなみに:=
は関数のデフォルト引数
>>> print!("こんちは,", end:="最後")
こんちは最後
今書いた部分をスクリプトとして保存して、erg <name>.er
ですぐに実行もできる
拡張子は.er
となっており、サフィックスのerとして使えるので名前と実行があったものができて良い(build.erやmodul.erなど)
演算
version0.6.4はRatio型が上手く機能していないので注意
型の基本と四則演算や比較はいつも通りの演算子で計算できる
# Nat型
>>> 0001 # 0が無視される
1
>>> 1_000 # `_`を付けれる
1000
# Ratio型(Float型になっている)
>>> 1.0
>>> .1 # 整数部分が0のときは省略できる
0.1
>>> 1. # 少数部分がない時も省略できる
1.0
# Exponential型(Ratio型の別表現)
>>> 1e-4
0.0001
>>> 1 + 1
2
>>> 1.0 + 1
2.0
>>> 10 / 3
3.3333333333333335
>>> 10 // 3
3
>>> 2.0 * 4
8.0
>>> 10 % 3 # version0.6.4ではバグがあるので注意
1
>>> True == 1.0
True
>>> 10 >= 10
True
>>> 10 < 3
False
Rustとは違い型が違っていても計算をすることができる
それはbool <: Nat <: Int <: Ratio(Exponecial) <: Complex
という感じで定義されているためである
部分型と汎化型の関係性
A <: Bは「型Aによる型Bの部分型宣言(subtype declaration)」を表します。またこの時の型Aを部分型(subtype)、型Bを汎化型(supertype)と言います。A <: Bであるとき、型Aを持つ全ての式は型Bをの特性を持ちます。これは、形式的には∀x: A (x: B)を意味します(これはErgの有効な式ではありません)。
これらの型と別で高速計算用のFloat型
を作成する計画がある
現状Ratio型もFloat型も同じRustのf64(f32)でつくられているのでRatio型を宣言してもFloat型になってしまう
上記のコードのいくつかは以下のように計算される
>>> 1 + 1.0 # 1(Int型)がRatio型にアップキャストされ、Ratio型 + Ratio型で計算される
2.0 # Ratio型
>>> True == 1.0 # True(Bool型)がRatio型にアップキャストされ、Ratio == Ratioで比較される
True
Rustと違いこのように自動で型変換をしてくれている
また、型を含めて宣言を行うことで間違った型を取るとコンパイル時にエラーとして検出することができる
>>> a: Nat = 1 # 成功
>>> b: Nat = -1 # -1はInt型
Error[#0052]: File <stdin>, line 1, <module>
1 | b: Nat = -1
: -
: |- expected: Nat
: `- but found: {-1, }
the type of b is mismatched
>>> b: Int = -1 # 成功
>>> c: Int = 1.0 # 1.0はRatio型
Error[#0052]: File <stdin>, line 1, <module>
1 | c: Int = 1.0
: -
: |- expected: Int
: `- but found: {1.0f, }
the type of c is mismatched
pythonと違い、型を宣言したらきちんとコンパイルエラーとして検出できる
こんな感じでRustとpythonのいいとこどりをしている
ループ
プロシージャのfor!
とwhile!
がある
プロシージャと関数の関係はfunc <: proc
なのでfor!
とwhile!
を使うことで関数を引数として取ることもできる
- for!ループ
範囲はbegin..<end
で右開区間になっており、begin..end
で閉区間となっている
順序(Ord)が保証されているならend..<begin
で、右開区間のみ逆順にとることもできる(まだできない)
# for! イテレータ, (無名プロシージャ)=>
# 処理
>>> for! 0..<5, i => # 右開区間
... print! i
...
0
1
2
3
4
>>> for! 0..5, j => # 閉区間
... print! j, end:=""
...
012345
上記に述べた通りfunc <: proc
の関係になっているので副作用を許すプロシージャfor!
は->(無名関数)
を取ることもできる
もちろん内部で副作用がある変数やプロシージャを使うとエラーとなる
# for! イテレータ, (parm) ->
# 副作用のない処理
>>> for! 0..<5, i -> # =>ではないので注意
... log i # 副作用無しの値の出力
...
0
1
2
3
4
>>> for! 0..<5, i ->
... print! i
...
Error[#0340]: File <stdin>, line 2, ::stdin::<lambda>
2 | print! i
: --------
this expression causes a side-effect
- while!
for!と違い無名プロシージャ(関数)は引数を取らない
do
, do!
はそれぞれ()->
と()=>
の糖衣構文となっている
# while! ブロック(条件式), () =>
# 処理
>>> while! do!(False), do!: # Trueにすると無限ループ
... print! "hi"
...
>>> a = !0
>>> while! do!(a < 5), do!:
... a.inc!()
... print! a
...
1
2
3
4
5
比較とパターンマッチ
if
とif!
、match
、match!
がある
- if, if!
たぶんelse if
はなく、それが必要な場合はmatch
を使った方が書きやすいと思う
# if ブール, () ->
# 副作用のない処理
>>> if True, do:
... log "True"
...
True
# if! ブール, () =>
# 処理
>>> if! True, do!:
... print! "True"
...
True
>>> if False: # ブロックではなく一行でも書ける
... do log 1
... do log 2
2
>>> res = if! False: # ブロックで書くこともできる
... do! print! ""
... do!:
... print! "False"
...
...
>>> res
False
ergでは多くが式になっているのでブロックの最後が返り値となる
>>> result = if True, do:
... log "True was chosen"
... 1
...
True was chosen
>>> assert! result == 1
- match, match!
REPLだとまだ評価しきれないのでエラーになる(version0.6.0で修正中)
修正した(ver0.6.4)
基本的に引数は全ての可能性を網羅していないといけない。
数値型は全てを網羅できないので、_
のプレースホルダ―を利用することで網羅性を確保できます。
n: Nat = 13
match n:
1 => print! 1
2 => print! 2
13 => print! 13
_ => print! "others"
複数のパターンマッチや範囲などはまだできないがこれから実装される予定
ref to: rust match
n: Nat = 13
match n:
1 => log "one"
_: {2, 3, 5, 7, 11 } => log "prime"
_: 13..20 => log "toon"
_ => log "Ain't special"
for! 1..<100, i =>
match! [i % 3, i % 5]:
[0, 0] => print! "FizzBuzz"
[0, _] => print! "Fizz"
[_, 0] => print! "Buzz"
[_, _] => print! i
予約語
ergでは予約語がない
一応特別な扱いを受ける識別子(組込み定数や関数、プロシージャなど)はあるが予約語ではない
組込み定数
定数なので上書きできない
- True, False
- None
etc...
組込み関数/プロシージャ
予約語ではないため識別子としても使えてしまう
- if
- for
- print!
- if!
- for!
- open!
etc...
>>> for = None
>>> for
None
>>> print! = Inf
>>> print!
Inf
こんな感じで上書きできてしまうので、意図しない挙動になったりする可能性があるので気を付ける
コレクション
原則的には不変でかつ複数のオブジェクトを扱える
- array
- tuple
- dict
- record
- set
可変にしたければ!
をつける必要がある
version0.6.4では可変で宣言はできるが変更の際にエラーが出る
# Array Literal
>>> a = [1, 2, 3, 4, 5] # 原則として同じ種類のオブジェクトしか保持できない
>>> a[0] = 10 # 不変なのでエラー
Error[#2309]: File <stdin>, line 1,
1 | a[10] = 10
: -----
invalid syntax
>>> a = ![1, 2, 3]
>>> a.push! 1
>>> a
[1, 2, 3, 1]
# Tuple literal
>>> b = (1, 1.0, True, "a", a) # 複数種類のオブジェクトを保持できる
>>> b
(1, 1.0, True, 'a', [1, 2, 3, 4, 5])
# Dict Liteal
>>> c = {"hi": 2, "there": 5} # keyとvalueのペアを持てる、それぞれの型は一致していないといけない
>>> c["there"]
5
# Record Literal
>>> john = {.name = "John Doe"; age = 21} # 公開(name)・非公開(age)を設定でき、フィールド名にあったのを取り出せる
>>> john.name # REPLなのでageもアクセスできるが他のスクリプトからはアクセスできない
"John Doe"
# Set literal
>>> d = {1, 2, 2, 2, 3, 1, 3}
>>> d
{1, 2, 3}
# Set Literal with type specify
>>> e: {Int; 4} = {1, 2, 3, 3} # 重複が消えてエラーになる
Error[#0052]: File <stdin>, line 1, <module>
1 | e: {Int; 4} = {1, 2, 3, 3}
: -
: |- expected: Set(Int, 4)
: `- but found: Set({1, 3, 2, }, 3)
the type of e is mismatched
また、pythonと同様に複数の型を指定することもできるがその際には明示的にor
で型をつける必要がある
今はまだできないがこれからできる予定
>>> a: [Str or Int] = ["a", 1, "b", 2]
Error[#3003]: File <stdin>, line 1
1 │ a: [Str or Int] = ["a", 1, "b", 2]
: ------------
: `─ SyntaxError: 不正な構文です
これらコレクションを扱うAPIなどはまだ作られていないはず
操作がいくつかできてきたので紹介する
# 適当な可変の配列を用意
>>> arr = ![10, 134, 9, 42, 414, 100, 313]
>>> arr.push! 1 # 後ろに1を追加
>>> arr.pop!() # 後ろの1を取り出す(削除)
1
>>> arr.sort!() # 破壊的なソート
>>> arr
[9, 10, 42, 100, 134, 313, 414]
などなどできてきた
関数
REPLだと書きにくいのでスクリプトを書いていく
また、既にvscode-ergの拡張とLSPのls2つが存在するので、色付けやシンタックスエラーなのどの検出などをやってくれる
# cargo install els
cargo install erg # これでelsもインストールされるようになった
関数名、仮引数と計算方法の3つがあれば関数として扱える
# 関数名(引数1: 型, 引数2: 型,...): 返り値の型 = 計算などの処理
add x, y = x + y # private
print! add 1, 2 # 3
.sub(x, y) = # 改行もできる
# スペースが統一されていればブロックとして認識される
x - y # 最後の行の式がそのまま返り値になる
print! sub(1, 2) # -1
# なんかもっと良いのが思いつけばよかった
void(a, b, c) =
log a, b, c # これの返り値もNoneなので別にいらない
None # 明示的にNoneを返す
基本はこれだけしかない
print! add(1, 2) # ok
print! add 1000, 10000 # ok
print! add(add(1, 2), add(3, 4)) # Ok
# add(add(1, 2, add(3, 4)))ととれたりadd(add(1, 2, add) 3, 4)ととれたり、括弧の範囲が曖昧なのでエラー
print! add add 1, 2, add 3, 4 # error
もちろんこれに型をつけることもできる
sub(x: Int, y:Int): Int = x - y
print! sub 10, -9 # 19
基本的にはダウンキャストは安全ではないので明示的にダウンキャストしない限りエラーになる
現状はダウンキャストはないはず
アップキャストはできるので汎化型ならエラーにならない
sub(x: Int, y: Int): Nat = x - y # エラー
sub(x: Int, y: Int): Ratio = x - y # 成功
実引数時の:(コロン)
関数はfunc_name arg1, arg2, .. argn
という形で呼び出す
そこで実引数に計算をしながら代入したり、そもそも長い変数名だったりすると一行が長くなってしまう
add x, y = x + y
add some_long_name_variable_1 + some_long_name_variable_2, some_long_name_variable_3 * some_long_name_variable_4
そこで、一行が長くなってしまうので:
を使って改行を入れることができる
add some_long_name_variable_1 + some_long_name_variable_2:
some_long_name_variable_3 * some_long_name_variable_4
さきほどまで使っていたif!
やfor!
も組み込みの関数やプロシージャなので実引数を取る
なので、多くの場合で複数行にまたがる処理を書く必要があるため、一行ではなく:
を使った複数行のスタイルになっている
if! condition, (() -> expr_1), (() -> expr_2)
if! condition, do!:
expr_1
if! condition:
do expr_1
do expr_2
# for!の返り値はNone
for! iter, (parm) => expr
for! iter, parm =>
expr
ただしfor!
は引数ありのproc
を受け取るのでそこは注意が必要
for!((0..3, i) () => expr) => None # こうではなく
for!((0..3), (i => expr)) => None # こうなる
高階関数
今までの関数の定義と同じでそのまま、関数を受け取れる
add x = y -> x + y
assert add 10 10 == 20
もちろん返り値として関数を返す以外にも引数として受け取ることもできる
f = i = i * 100
g f = f 100
assert g f == 10000
特に指定する必要なく勝手に推論して関数を受け取ったり返したりしている
ただ、これだと意図しない挙動をする場合があるので、きちんと引数や返り値に型を明示的に書くことをお勧めする
just(x: (Int -> Int)): (Int -> Int) = x # 関数を受け取り,受けとった関数を返す
無名関数・プロシージャ
無名関数は->
,プロシージャは=>
の演算子を使う
for!
やif!
の時にでてきたdo
やdo!
はこの無名関数と無名プロシージャの糖衣構文になっている
do
は() ->
、do!
は() =>
で引数を取らずに作ることができる
これら演算子は左辺に引数を一つだけ取ることができる
# 無名関数は`->`を使う
f = x -> x + 1
print! f 1 # 2
f x = x + 1 # 関数だとこれと同じ
# 無名プロシージャは`=>`を使う
g! = x => print! x + 100
g! 100 # 200
g! x = print! x + 100 # プロシージャだこれと同じ
ちなみに変数に代入しないとただの宣言になるので高階関数とか以外では使えないはず
>>> x -> x + 1
<function <lambda> at 0x0000017673F71A80>
>>> x 1
Error[#0384]: File <stdin>, line 1, <module>
1 | x 1
: -
x is not defined
複数の引数を取りたいときには明示的に(タプル)
を使う
f = (x, y) -> log x + y
f 1, 1 # 2
g! = (x, y) => print! x * y
g! 2, 2 # 4
無名関数で引数を指定しない場合は遅延評価として扱われる
time = import "time"
date = import "datetime"
now = if! True:
do!:
time.sleep! 1000
date.now!()
do date.new("1970", "1", "1", "00", "00") # 実行されない
クロージャ
ただの関数ではなく、外部の変数を扱っている
math = pyimport "math"
sin(x) = math.lsin(math.pi*x/360)
print! sin(90) # 1.0
外部変数を扱えるため、可変の変数もキャプチャできてしまう
これにより関数は副作用を許さないのに変数の値によって返り値が変化してしまう
a: Nat = !1 # 可変変数
f(x) = a * x - 2
print! f(2) # 0
a.update! x -> x + 1
# 異なる返り値になる
print! f(2) # 2
erg
ではこれはエラーになる
なので、外部変数が副作用を許さない・不変状態にする必要がある
a: Nat = !1 # 可変
immut_a = a.clone().freeze() # aを複製し不変化する
g(x) = immut_a * x - 2
print! g(2) # 0
a.update! x -> x + 1
# 異なる返り値になる
print! g(2) # 0
デフォルト引数を取るときにはprint!
の時に出てきた:=
を利用する
add(x, y:=1000) = x + y
print! add 1, 2 # 3
print! add 1 # 1001
キーワード引数と可変長引数は現状実はまだ装されていないが、計画としてある
クラス
クラスにはClass
の指定が必要になる
また、フィールドを上記のrecordで実装する
クラス名は基本的にはじめを大文字にする
version0.6.0からわりとできるようになってきた
# Name = Class { field1 = Type; field2 = Type; ... }
Person = Class { name = Str; .age = Int } # 要素属性
Person::
species = "Human" # クラス属性
少し込み入った話になる
要素属性(レコード内で定義した属性)と型属性(クラスの場合は特にインスタンス属性/クラス属性とも呼ばれる)は全くの別物である。型属性は型自体の持つ属性である。型の要素は、自らの中に目当ての属性がないときに型属性を参照する。要素属性は要素が直接持つ固有の属性である。 なぜこのような区分けがされているか。仮に全てが要素属性だと、オブジェクトを生成した際に全ての属性を複製・初期化する必要があり、非効率であるためである。 また、このように分けたほうが「この属性は共用」「この属性は別々に持つ」などの役割が明確になる。
nameやageはそれぞれのインスタンス毎に異なるのでこういうのは要素属性にするべきで、speciesのようにクラス全体で共通な属性はクラス属性として共有にすることができる。
実際、Person
というClassでspecies
がHuman
以外だったら人間に化けた外星人になるのでAlien Classに分けた方が良い
Ergのクラスは基本的には継承することができないので,継承をしたければデコレータで継承できるように指定する
@Inheritable
Point2D = Class {x = Int; y = Int}
継承する側もきちんとそれを明記して,追加の属性があればそれを代入演算子を使ってレコードに追加する
Point3D = Inherit Point2D, Additional := {z = Int}
メソッドをoverrideするときも同様にデコレータを利用して明示的に上書きすることを指定する
Point2D.
norm self = self::x**2 + self::y**2
Point3D.
@Override
norm self = self::x**2 + self::y**2 + self::z**2
p2d = Point2D.new {x = 1; y = 2}
print! p2d.norm() # 5
p3d = Point3D.new {x = 1; y = 2; z = 3}
print! p3d.norm() # 14
見にくいが,こんな感じでばらばらで定義したり使ったりできる
@Inheritable
Point2D = Class {x = Int; y = Int}
Point3D = Inherit Point2D, Additional := {z = Int}
Point2D.
norm self = self::x**2 + self::y**2
Point3D.
@Override
norm self = self::x**2 + self::y**2 + self::z**2
p2d = Point2D.new {x = 1; y = 2}
p3d = Point3D.new {x = 1; y = 2; z = 3}
print! p2d.norm()
print! p3d.norm()
トレイト
トレイトは、レコード型に型属性の要求を追加した記名型
まだできていないが以下のようにメソッド名とその返り値を規定したりできる
Norm = Trait {.norm = (self: Self) -> Int}
そしてこの型を持つクラスを実装することでNormの部分型としてみなすことができる
Point2D = Class {.x = Int; .y = Int}
Point2D|<: Norm|.
norm self = self.x**2 + self.y**2
Point3D = Class {.x = Int; .y = Int; .z = Int}
Point3D|<: Norm|.
norm self = self.x**2 + self.y**2 + self.z**2
モジュールのインポート
先ほどの関数を他のスクリプトにインポートすることもできる
pythonと異なり、ergは基本的に非公開なので明示的に公開にしないといけない
関数も変数なので、変数名の前に.
をつけることで公開にすることができる
- sampl.er
add(x, y) = x + y # private
.sub(x, y) = x - y # public
private = "private" # private
.public = "public" # public
- main.er
sample = import "sample"
print! sample.sub 1, 2 # subはpublicなのでできる
print! sample.public # これもできる
print! sample.add 1, 2 # addはprivateなのでエラー
$ erg main.er
Error[#0658]: File .\main.er, line 4, in <module>
4 │ print! sample.add(1, 2)
----
╰─ AttributeError: sample(: Module("sample"))にaddという属性はありません
print! sample.private # これもエラー
Error[#0496]: File .\main.er, line 5, in <module>
5 │ print! sample.private
-------
╰─ AttributeError: Module("sample")型オブジェクトにprivateという属性はありません
pythonとの互換性
ergはpython3.11~3.7との互換性を持っている (他のバージョンだと動かないはず)
なので、pythonの組込み関数やモジュールなどを使うこともできる
将来的にはユーザーが作成したモジュールやnumpyなどのライブラリのインポートもできるようになる
- datetime
- glob
- http
- importlib
- io
- json
- math
- os
- posix
- random
- re
- shutil
- socket
- string
- subprocess
- sys
- tarfile
- time
- urllib
- zipfile
etc...
実際にmath
をインポートしてみる
pythonのスクリプトやモジュールをインポートするにはpyimport
を利用し、これを変数に代入する
math = pyimport "math"
print! math.pi # 3.14...
print! math.e # 2.71...
print! math.sin(math.pi / 2) # 1.0
glob
のように副作用がある場合には元のglob
に副作用を示す!
をつけて利用する
glob = pyimport "glob"
for! glob.glob!("*"), i =>
print! i # main.er, main.pyc
このようにしてpythonのモジュールを利用することができる
また、まだ少ないが組込みプロシージャwith!
とopen!
もある
glob = pyimport "glob"
open_file!(pth) =
with! open!(pth, mode:="r", encoding:="utf_8"), f =>
data = f.read!()
data
main!() =
paths = glob.glob! "*.er"
for! paths, pth =>
file = open_file! pth
print! file # このファイルと他の.erファイルが表示される
main!()
このような感じでファイルの操作もできる
同様に.er
ファイルをコンパイルしてpython側から呼び出すこともできる
erg --mode --compile main.er
# ソースコードからだと
cargo run -- --mode --comple
するとmain.pyc
が生成されるはずなのでこれで呼び出せる
$ python
Python 3.11.1 [MSC v.1932 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import main
glob = pyimport "glob"
open_file!(pth) =
with! open!(pth, mode:="r", encoding:="utf_8"), f =>
data = f.read!()
data
main!() =
paths = glob.glob! "*.er"
for! paths, pth =>
file = open_file! pth
print! file # このファイル
main.erのファイルが表示される
このようにpython側からもすぐに呼び出せるようになっている
Transcompile
version0.6.0以降だったはず
Ergをpythonにトランスコンパイルすることもできる
erg --mode transpile
cargo run -- --mode transpile
内容は省略するが,結構長いのが出力される
Conclusion
Rust製の静的型付け言語のErgを紹介してきた
軽くではあったが、大まかな説明をすることはできたと思う
現状まだ、基本文法でもバグがあったりするのでテストコードを書かないといけないなあと常々思っている
なのでテストコードなどの上手な書き方を教えてくれる人がいたら嬉しい
まだオブジェクト指向や関数型の特徴など紹介しきれていない箇所もあるが、私がまだこの言語の奥を覗き込めていないのでここまでになる
私のようにRustやpythonに興味があり、言語作りをやってみたい人はぜひErg言語の開発に参加することをお勧めしたい