18
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Pythonライクな新しい静的型付け言語Ergの紹介

Last updated at Posted at 2022-10-23

erg version 0.5.11
erg version 0.6.0
erg version 0.6.4 <- ここ
python version 3.11.0 推奨(他だとバグが多い)

:point_right: Github

:point_right: The Erg Book

:point_right: webpage

:point_right: shibaさんのzenn記事

個人的にずっと気になってちょくちょくドキュメントや実装のお手伝いをしている

新しい言語が作られていくのが見ていてとても楽しい

内部的にはこうなっているのかという仕組みを学べて楽しい

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

比較とパターンマッチ

ifif!matchmatch!がある

  • 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もインストールされるようになった

vscode_erg.png

関数名、仮引数と計算方法の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!の時にでてきたdodo!はこの無名関数と無名プロシージャの糖衣構文になっている

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でspeciesHuman以外だったら人間に化けた外星人になるので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
sampl.er
add(x, y) = x + y # private
.sub(x, y) = x - y # public
private = "private" # private
.public = "public" # public
  • main.er
python.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などのライブラリのインポートもできるようになる

pythonの組込みモジュールの一覧

  • 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言語の開発に参加することをお勧めしたい

18
10
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
18
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?