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

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

:point_right: [Github](https://github.com/erg-lang/erg)

:point_right: [The Erg Book](https://erg-lang.org/the-erg-book/JA/)

:point_right: [webpage](https://erg-lang.org/)

:point_right: [shibaさんのzenn記事](https://zenn.dev/mtshiba/articles/a38c9fcd9646d4)

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

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

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

pythonに型を宣言して書けるという喜び

ergには[discord](https://discord.gg/zfAAUbgGr4)もあるのでぜひ参加してみてください

## Summary

:::note warn
まだ開発途中なので、変更や未実装な部分がたくさんあります
上手く動作しなかったり、panicを起こす可能性があります
:::

ergはpythonと互換性を持つ静的型付け言語

特徴をあげると以下の通り

- pythonとの互換性
- lightなRust言語
- 強力な型推論
- 型完全
- オブジェクト指向言語であり、関数型言語でもある

基礎はもう出来上がっているので簡単なスクリプトは書ける

## Install

まだ、パッケージマネージャなどはなくコンパラのみになっている

[Release](https://github.com/erg-lang/erg/releases)からダウンロードできる

Rust製なのでcargoでインストールもできる

```shell
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を作ってください)

ソースからビルドする

```shell
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`でコンパイラを使うことができる

```shell
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で終了などの機能が使えるようになる

```sh
> erg
Starting the REPL server...
Connecting to the REPL server...
Erg interpreter 0.6.0 on x86_64/windows
>>> a =
...     1 
...     # 空欄の改行を入れるとディデントされる
>>>
```

とりあえずHello, worldをしてみる

main関数を実装したりする必要がなく、これだけで実行ができる

```python
>>> print!("hello, world") # コメント
hello, world
```
コメントは`#`で複数行コメントは`#[`~`]#`になる

`!`は副作用のあるプロシージャであることを示している([詳細](https://erg-lang.org/the-erg-book/JA/07_side_effect.html))

ergでは括弧に曖昧性がなければ省略して書くことができる([参照](https://github.com/erg-lang/erg/blob/main/doc/JA/syntax/00_basic.md#hello-world))

```python
>>> print! 1, "a", True, [1, 2, 3]
1 a True [1, 2, 3]
```

`end:="something"`引数を取って改行の代わりに別のを代入することもできる

ちなみに`:=`は関数のデフォルト引数

```python
>>> print!("こんちは，", end:="最後")
こんちは最後
```

今書いた部分をスクリプトとして保存して、`erg <name>.er`ですぐに実行もできる

拡張子は`.er`となっており、サフィックスのerとして使えるので名前と実行があったものができて良い(build.erやmodul.erなど)

## 演算

:::note warn
version0.6.4はRatio型が上手く機能していないので注意
:::

型の基本と四則演算や比較はいつも通りの演算子で計算できる

```python
# 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`という感じで定義されているためである

[部分型と汎化型](https://github.com/erg-lang/erg/blob/main/doc/JA/API/types/traits/Num.md)の関係性

> 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型になってしまう

上記のコードのいくつかは以下のように計算される

```python
>>> 1 + 1.0 # 1(Int型)がRatio型にアップキャストされ、Ratio型 + Ratio型で計算される
2.0 # Ratio型
>>> True == 1.0 # True(Bool型)がRatio型にアップキャストされ、Ratio == Ratioで比較される
True
```

Rustと違いこのように自動で型変換をしてくれている

また、型を含めて宣言を行うことで間違った型を取るとコンパイル時にエラーとして検出することができる

```shell
>>> 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`で、右開区間のみ逆順にとることもできる(まだできない)
 
```shell
# for! イテレータ, (無名プロシージャ)=>
#    処理
>>> for! 0..<5, i => # 右開区間
...     print! i
...
0
1
2
3
4
>>> for! 0..5, j => # 閉区間
...    print! j, end:=""
...
012345
```

上記に述べた通り`func <: proc`の関係になっているので副作用を許すプロシージャ`for!`は`->(無名関数)`を取ることもできる

もちろん内部で副作用がある変数やプロシージャを使うとエラーとなる

```shell
# 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!`はそれぞれ`()->`と`()=>`の糖衣構文となっている

```shell
# 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`を使った方が書きやすいと思う

```shell
# 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では多くが式になっているのでブロックの最後が返り値となる

```shell
>>> result = if True, do:
...     log "True was chosen"
...     1
...
True was chosen
>>> assert! result == 1
```

- match, match!

~~REPLだとまだ評価しきれないのでエラーになる(version0.6.0で修正中)~~ 
修正した(ver0.6.4)

基本的に引数は全ての可能性を網羅していないといけない。

数値型は全てを網羅できないので、`_`のプレースホルダ―を利用することで網羅性を確保できます。

```python
n: Nat = 13
match n:
    1 => print! 1
    2 => print! 2
    13 => print! 13
    _ => print! "others"
```

複数のパターンマッチや範囲などはまだできないがこれから実装される予定

ref to: [rust match](https://doc.rust-lang.org/rust-by-example/flow_control/match.html)

```python
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...

```shell
>>> for = None

>>> for
None
>>> print! = Inf

>>> print!
Inf
```

こんな感じで上書きできてしまうので、意図しない挙動になったりする可能性があるので気を付ける

## コレクション

原則的には不変でかつ複数のオブジェクトを扱える

- array
- tuple
- dict
- record
- set

可変にしたければ`!`をつける必要がある

version0.6.4では可変で宣言はできるが変更の際にエラーが出る

```python
# 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`で型をつける必要がある

今はまだできないがこれからできる予定

```python
>>> 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](https://github.com/erg-lang/erg/blob/main/doc/JA/API/index.md)などはまだ作られていないはず~~

- [Array!](https://github.com/erg-lang/erg/blob/main/doc/JA/API/types/classes/Array!(T%2CN).md)

操作がいくつかできてきたので紹介する

```python
# 適当な可変の配列を用意
>>> 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](https://github.com/erg-lang/vscode-erg)の拡張とLSPのls2つが存在するので、色付けやシンタックスエラーなのどの検出などをやってくれる

```shell
# cargo install els
cargo install erg # これでelsもインストールされるようになった
```

![vscode_erg.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2630694/30ffa952-031a-8847-eadb-6ba20ee8c55e.png)


関数名、仮引数と計算方法の3つがあれば関数として扱える

```python
# 関数名(引数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を返す
```

基本はこれだけしかない

```python
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
```

もちろんこれに型をつけることもできる

```python
sub(x: Int, y:Int): Int = x - y
print! sub 10, -9 # 19
```

基本的にはダウンキャストは安全ではないので明示的にダウンキャストしない限りエラーになる

現状はダウンキャストはないはず

アップキャストはできるので汎化型ならエラーにならない

```python
sub(x: Int, y: Int): Nat = x - y # エラー
sub(x: Int, y: Int): Ratio = x - y # 成功
```

### 実引数時の`:(コロン)`

関数は`func_name arg1, arg2, .. argn`という形で呼び出す

そこで実引数に計算をしながら代入したり、そもそも長い変数名だったりすると一行が長くなってしまう

```python
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
```

そこで、一行が長くなってしまうので`:`を使って改行を入れることができる

```python
add some_long_name_variable_1 + some_long_name_variable_2:
    some_long_name_variable_3 * some_long_name_variable_4
```

さきほどまで使っていた`if!`や`for!`も組み込みの関数やプロシージャなので実引数を取る

なので、多くの場合で複数行にまたがる処理を書く必要があるため、一行ではなく`:`を使った複数行のスタイルになっている

```python
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`を受け取るのでそこは注意が必要

```python
for!((0..3, i) () => expr) => None # こうではなく
for!((0..3), (i => expr)) => None # こうなる
```

### 高階関数

今までの関数の定義と同じでそのまま、関数を受け取れる

```python
add x = y -> x + y
assert add 10 10 == 20
```

もちろん返り値として関数を返す以外にも引数として受け取ることもできる

```python
f = i = i * 100
g f = f 100
assert g f == 10000
```

特に指定する必要なく勝手に推論して関数を受け取ったり返したりしている

ただ、これだと意図しない挙動をする場合があるので、きちんと引数や返り値に型を明示的に書くことをお勧めする

```python
just(x: (Int -> Int)): (Int -> Int) = x # 関数を受け取り，受けとった関数を返す
```

### 無名関数・プロシージャ

無名関数は`->`，プロシージャは`=>`の演算子を使う

`for!`や`if!`の時にでてきた`do`や`do!`はこの無名関数と無名プロシージャの糖衣構文になっている

`do`は`() ->`、`do!`は`() =>`で引数を取らずに作ることができる

これら演算子は左辺に引数を一つだけ取ることができる

```python
# 無名関数は`->`を使う
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 # プロシージャだこれと同じ
```

ちなみに変数に代入しないとただの宣言になるので高階関数とか以外では使えないはず

```python
>>> x -> x + 1
<function <lambda> at 0x0000017673F71A80>
>>> x 1
Error[#0384]: File <stdin>, line 1, <module>

1 | x 1
  : -

x is not defined
```

複数の引数を取りたいときには明示的に`(タプル)`を使う

```python
f = (x, y) -> log x + y
f 1, 1 # 2
g! = (x, y) => print! x * y
g! 2, 2 # 4
```

無名関数で引数を指定しない場合は遅延評価として扱われる

```python
time = import "time"
date = import "datetime"
now = if! True:
    do!:
        time.sleep! 1000
        date.now!()
    do date.new("1970", "1", "1", "00", "00") # 実行されない
```

### クロージャ

ただの関数ではなく、外部の変数を扱っている

```python
math = pyimport "math"
sin(x) = math.lsin(math.pi*x/360)
print! sin(90) # 1.0
```

外部変数を扱えるため、可変の変数もキャプチャできてしまう

これにより関数は副作用を許さないのに変数の値によって返り値が変化してしまう

```python
a: Nat = !1 # 可変変数
f(x) = a * x - 2
print! f(2) # 0
a.update! x -> x + 1
# 異なる返り値になる
print! f(2) # 2
```

`erg`ではこれはエラーになる

なので、外部変数が副作用を許さない・不変状態にする必要がある

```python
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!`の時に出てきた`:=`を利用する

```python
add(x, y:=1000) = x + y

print! add 1, 2 # 3
print! add 1 # 1001
```

キーワード引数と可変長引数は現状実はまだ装されていないが、計画としてある

## クラス

クラスには`Class`の指定が必要になる

また、フィールドを上記のrecordで実装する

クラス名は基本的にはじめを大文字にする

version0.6.0からわりとできるようになってきた

```python
# 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のクラスは基本的には継承することができないので，継承をしたければデコレータで継承できるように指定する

```python
@Inheritable
Point2D = Class {x = Int; y = Int}
```

継承する側もきちんとそれを明記して，追加の属性があればそれを代入演算子を使ってレコードに追加する

```python
Point3D = Inherit Point2D, Additional := {z = Int}
```

メソッドをoverrideするときも同様にデコレータを利用して明示的に上書きすることを指定する

```python
Point2D.
    norm self = self::x**2 + self::y**2

Point3D.
    @Override
    norm self = self::x**2 + self::y**2 + self::z**2    
```

```python
p2d = Point2D.new {x = 1; y = 2}
print! p2d.norm() # 5
p3d = Point3D.new {x = 1; y = 2; z = 3}
print! p3d.norm() # 14
```

見にくいが，こんな感じでばらばらで定義したり使ったりできる

```python
@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()
```

## トレイト

トレイトは、レコード型に型属性の要求を追加した記名型

まだできていないが以下のようにメソッド名とその返り値を規定したりできる

```python
Norm = Trait {.norm = (self: Self) -> Int}
```

そしてこの型を持つクラスを実装することでNormの部分型としてみなすことができる

```python
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
```python: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の組込みモジュールの一覧](https://github.com/erg-lang/erg/blob/main/compiler/erg_common/python_util.rs)

- datetime
- glob
- http
- importlib
- io
- json
- math
- os
- posix
- random
- re
- shutil
- socket
- string
- subprocess
- sys
- tarfile
- time
- urllib
- zipfile
 etc...

実際に`math`をインポートしてみる

pythonのスクリプトやモジュールをインポートするには`pyimport`を利用し、これを変数に代入する

```python
math = pyimport "math"
print! math.pi # 3.14...
print! math.e # 2.71...
print! math.sin(math.pi / 2) # 1.0
```

`glob`のように副作用がある場合には元の`glob`に副作用を示す`!`をつけて利用する

```python
glob = pyimport "glob"
for! glob.glob!("*"), i =>
    print! i # main.er, main.pyc
```

このようにしてpythonのモジュールを利用することができる

また、まだ少ないが組込みプロシージャ`with!`と`open!`もある

```python
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側から呼び出すこともできる

```shell
erg --mode --compile main.er
# ソースコードからだと
cargo run -- --mode --comple
```

すると`main.pyc`が生成されるはずなのでこれで呼び出せる

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