initial commit

This commit is contained in:
Shunsuke Shibayama 2022-08-10 23:02:27 +09:00
commit 96132b20f6
346 changed files with 36806 additions and 0 deletions

2
.gitattributes vendored Normal file
View file

@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto

22
.github/workflows/rust.yml vendored Normal file
View file

@ -0,0 +1,22 @@
name: Rust
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
env:
CARGO_TERM_COLOR: always
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Build
run: cargo build --verbose
- name: Run tests
run: cargo test --verbose

18
.gitignore vendored Normal file
View file

@ -0,0 +1,18 @@
# Generated by Cargo
# will have compiled files and executables
/target/
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk
*.pyc
/.vscode/
/.VSCodeCounter/
/.vs/
/.DS_Store
/*/.DS_Store
/.idea/
/timeit.dat

42
Cargo.toml Normal file
View file

@ -0,0 +1,42 @@
[package]
name = "erg"
version = "0.1.0"
description = "The Erg programming language"
authors = ["Shunsuke Shibayama <sbym1346@gmail.com>"]
license = "MIT OR Apache-2.0"
edition = "2021"
[features]
# when "debug" feature is turned on, that of the following crates will also be turned on.
debug = [
"common/debug",
"parser/debug",
"compiler/debug",
#"vm/debug"
]
japanese = [
"common/japanese",
"parser/japanese",
"compiler/japanese",
#"vm/japanese"
]
[dependencies]
common = { path = "./src/common" }
parser = { path = "./src/compiler/parser" }
compiler = { path = "./src/compiler" }
# vm = { path = "./src/vm" }
# [workspace]
# member = ["cm", "dyne"]
# [profile.release]
# panic = 'abort'
# [[bin]]
# name = "cm"
# path = "src/compiler/main.rs"
# [[bin]]
# name = "dyne"
# path = "src/vm/main.rs"

176
LICENSE-APACHE Normal file
View file

@ -0,0 +1,176 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

17
LICENSE-MIT Normal file
View file

@ -0,0 +1,17 @@
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

184
README.md Normal file
View file

@ -0,0 +1,184 @@
# The Erg Programming Language
<div align="center">
<img width="500" src="./assets/erg_logo_with_slogan.svg">
</div>
<br>[Erg](https://mtshiba.github.io/TheErgBook) is a statically typed language that is Python compatible.
<p align='center'>
<img alt="Build status" src="https://github.com/mtshiba/erg/actions/workflows/rust.yml/badge.svg">
<a href="https://opensource.org/licenses/Apache-2.0"><img alt="License: MIT & APACHE 2.0" src="https://img.shields.io/badge/license-Apache--2.0_OR_MIT-blue"></a><br>
English | <a href='./README_JA.md'>日本語</a>
</p>
## Erg can be recommended to a person that&colon;
* uses Python, but want Rust-like robustness and comfortable compiler support.
* and yet, doesn't need the verbose type specifications & memory management model like Rust.
* wants a simple and consistent language like ML.
* wants a practical general-purpose language with dependent/refinement types.
* wants a language like Scala that can be used both object-oriented and functional.
## Features
> Some features are not yet implemented. Please see [TODO.md](./TODO.md) for implementation status.
1. Robustness
Erg has a smart & powerful type system. For example, Erg can do null checking (Option type), division by zero and out-of-range addresses in arrays at compile time.
```python
rand = import "random"
l = [1, 2, 3]
assert l in [Nat; 3] # type checking
assert l in [1..3; 3] # more detailed
l2 = l.push(rand.choice! 0..10)
assert l2 in [0..10; 4]
assert l2 + [3, 5, 7] in [0..10; 7]
# This causes an IndexError, Erg can detect it at compile time
l2[10] # IndexError: `l2` has 7 elements but was accessed the 10th element
2.times! do!:
print! "hello, ", end: ""
# => hello, hello,
-2.times! do!:
print! "hello, ", end: ""
# TypeError: `.times!` is a method of `Nat` (0 or more Int), not `Int`
{Meter; Sec; meter; yard; sec; ...} = import "unit"
velocity x: Meter, t: Sec = x / t
v = velocity 3yard, 2sec # TypeError: the type of `x` was mismatched: expect `Meter`, found `Yard`
v = velocity 3meter, 2sec # v == 1.5 m/s
```
2. Simplicity
Erg consists of a very simple syntax, which can significantly reduce the amount of code compared to other languages. However, its functionality is not inferior to them.
Since the type inference system is powerful, you can code like a dynamically typed language.
```python
fib 0 = 0
fib 1 = 1
fib n = fib(n - 1) + fib(n - 2)
assert fib(10) == 55
```
In Erg, there are very few things that are treated as special; there are no reserved words.
even for and while expressions are just one of the subroutines, so this is possible.
```python
loop! block = while! True, block
# equals to `while! True, do! print! "hello"`
loop! do!:
print! "hello"
```
3. Functional & Object-oriented
Erg is a pure object-oriented language. Everything is an object; types, functions, and operators are all objects. On the other hand, Erg is also a functional language.
Erg requires some kinds of markers to be placed on code that causes side effects or changes internal state, which can localize the complexity of code. This will greatly improve the maintainability of your code.
```python
# Functional style (immutable), same as `.sorted()` in Python
immut_arr = [1, 3, 2]
assert immut_arr.sort() == [1, 2, 3]
# Object-oriented style (mutable)
mut_arr = ![1, 3, 2]
mut_arr.sort!()
assert mut_arr == [1, 2, 3]
i = !1
i.update! old -> old + 1
assert i == 2
# Functions cannot cause side effects
inc i: Int! =
i.update! old -> old + 1
# SyntaxError: cannot call a procedural method in a function
# hint: only methods of mutable types can change the state of objects
Counter! = Inherit Int!
Counter!.
new i: Int = Self!::__new__ !i
inc! ref! self =
self.update! old -> old + 1
c = Counter!.new 1
c.inc!()
assert c == 2
```
4. Interoperability
Erg is internally compatible with Python and can import the Python API at zero cost.
```python
# using built-in Python modules
math, time = pyimport "math", "time"
{sin; pi; ...} = math
# using an external Python module
Tqdm! = pyimport("tqdm").'tqdm'
print! sin pi # 1.2246467991473532e-16
for! Tqdm!.'__call__'(0..99), i =>
time.sleep! 0.01 * i
```
5. Readable Error Messages
Erg emphasizes the readability of error messages; Erg is a programmer-friendly language, ~~unlike C++.~~
```python
proc! x =
l = [1, 2, 3]
l.push!(x)
l
```
```console
Error[#12]: File example.er, line 3, in <module>::proc!
2│ l = [1, 2, 3]
3│ l.push!(x)
^^^^^
AttributeError: Array object has no attribute `.push!`
hint: in order to update the internal state of an object, make it mutable by using `!` operator
hint: `Array` has `push`, see https://erg-lang.org/docs/prelude/Array/##push for more information
hint: `Array!` has `push!`, see https://erg-lang.org/docs/prelude/Array!/##push! for more information
```
## Installation
### Installing by cargo (Rust package manager)
```sh
cargo install erg
```
### Installing by an installer
You can get an installer from Github releases.
### Building from source
Building from source code requires the Rust toolchain.
```sh
git clone https://github.com/erg-lang/erg.git
cd erg
cargo build --release
```
## Contribution
Contributions are always welcome!
If you have any questions, please feel free to ask them on the [Discord channel].
## License
Erg is distributed under the terms of both the MIT license and the Apache License (Version 2.0).
See [LICENSE-APACHE](./LICENSE-APACHE), [LICENSE-MIT](./LICENSE-MIT) for details.

182
README_JA.md Normal file
View file

@ -0,0 +1,182 @@
# The Erg Programming Language
<div align="center">
<img width="500" src="./assets/erg_logo_with_slogan.svg">
</div>
<br>[Erg](https://mtshiba.github.io/TheErgBook)はPython互換の静的型付け言語です。
<p align='center'>
<img alt="Build status" src="https://github.com/mtshiba/erg/actions/workflows/rust.yml/badge.svg">
<a href="https://opensource.org/licenses/Apache-2.0"><img alt="License: MIT & APACHE 2.0" src="https://img.shields.io/badge/license-Apache--2.0_OR_MIT-blue"></a><br>
<a href='./README.md'>English</a> | 日本語
</p>
## Ergはこんな人におすすめです&#58;
* Pythonを使用しているが、Rustのような静的型付き言語で堅牢かつ快適にコーディングしたい
* しかし、煩雑な型定義やメモリ管理は避けたい
* MLのようにシンプルで一貫性のある言語を使いたい
* 依存型/篩型を持つ実用的な汎用言語を使いたい
* Scalaのように関数型とオブジェクト指向が高度に融合された言語を使いたい
## 特徴
> いくつかの機能は未実装です。実装状況は[TODO.md](./TODO.md)を御覧ください。
1. 堅牢性
Ergは賢くパワフルな型システムを持っています。コンパイル時のnullチェック(Option型)はもちろん可能で、ゼロ除算や配列の範囲外アクセスまでもがコンパイル時に検出できます。
```python
rand = import "random"
l = [1, 2, 3]
assert l in [Int; 3] # 型チェック
assert l in [1..3; 3] # さらに詳細に
l2 = l.push(rand.choice! 0..10)
assert l2 in [0..10; 4]
assert l2 + [3, 5, 7] in [0..10; 7]
# これはIndexErrorを引き起こしますが、コンパイル時に検出できます
l2[10] # IndexError: `l2`は7つの要素を持っていますが、10番目の要素のアクセスしようとしています
2.times! do!:
print! "hello, ", end: ""
# => hello, hello,
-2.times! do!:
print! "hello,", end: ""
# TypeError: `.times!``Nat`(0以上のInt)のメソッドです、`Int`ではありません
{Meter; Sec; meter; yard; sec; ...} = import "unit"
velocity x: Meter, t: Sec = x / t
v = velocity 3yard, 2sec # TypeError: `x`の型が適合しません。`Meter`を予期しましたが、`Yard`が渡されました
v = velocity 3meter, 2sec # v == 1.5 m/s
```
2. 簡潔性
Ergはとてもシンプルな文法からなり、コードもシンプルに書き上げられます。しかし、その機能の豊富さは他の言語に劣りません。
型推論機構は非常に強力であり、まるで動的型付け言語かのように書くことができます。
```python
fib 0 = 0
fib 1 = 1
fib n = fib(n - 1) + fib(n - 2)
assert fib(10) == 55
```
Ergでは特別扱いされる構文要素がとても少なく、例えば予約語が一つもありません。以下のような芸当も可能です。
```python
loop! block = while! True, block
# `while! True, do! print! "hello"`と同じです
loop! do!:
print! "hello"
```
3. 関数型&オブジェクト指向
Ergは純粋なオブジェクト指向言語です。全てはオブジェクトであり、型、関数、演算子も例外ではありません。
一方、Ergは関数型言語でもあります。副作用を引き起こすコードには`!`を付けなくてはなりません。これは、副作用の局所化を意識させてくれます。
```python
# immutableな関数型スタイル、Pythonの`.sorted()`と同じです
immut_arr = [1, 3, 2]
assert immut_arr.sort() == [1, 2, 3]
# mutableなオブジェクト指向スタイル
mut_arr = ![1, 3, 2]
mut_arr.sort!()
assert mut_arr == [1, 2, 3]
i = !1
i.update! old -> old + 1
assert i == 2
# 関数は副作用を起こせません
inc i: Int! =
i.update! old -> old + 1
# SyntaxError: 関数の中でプロシージャルメソッドは呼び出せません
# ヒント: 可変型メソッドだけがオブジェクトの状態を変更できます
Counter! = Inherit Int!
Counter!.
new i: Int = Self!::__new__ !i
inc! ref! self =
self.update! old -> old + 1
c = Counter!.new 1
c.inc!()
assert c == 2
```
4. 相互運用性
Ergは内部的にPythonと互換性があり、PythonのAPIをゼロコストで呼び出すことが出来ます。
```python
# Pythonのビルトインモジュールを使います
math, time = pyimport "math", "time"
{sin; pi; ...} = math
# Pythonの外部モジュールを使います
Tqdm! = pyimport("tqdm").'tqdm'
print! sin pi # 1.2246467991473532e-16
for! Tqdm!.'__call__'(0..99), i =>
time.sleep! 0.01 * i
```
5. 読みやすいエラーメッセージ
Ergはエラーメッセージの読みやすさを重視しています。Ergはプログラマに寄り添う言語であり、~~C++のように~~訳のわからない呪文を吐いたりはしません。
```python
proc! x =
l = [1, 2, 3]
l.push!(x)
l
```
```console
Error[#12]: ファイル example.er, 3行目, <module>::proc!
2│ l = [1, 2, 3]
3│ l.push!(x)
^^^^^
AttributeError: Arrayオブジェクトは`.push!`という属性を持っていません
ヒント: オブジェクトの内部状態を変更したい場合は、`!`演算子を使って可変化してください
ヒント: `Array``push`メソッドを持っています、詳しくは https://erg-lang.org/docs/prelude/Array/##push を参照してください
ヒント: `Array!``push!`メソッドを持っています、詳しくは https://erg-lang.org/docs/prelude/Array!/##push! を参照してください
```
## インストール
### cargo(Rustパッケージマネージャ)によるインストール
```sh
cargo install erg
```
### インストーラによるインストール
GitHub releaseからインストーラを入手できます。
### ソースコードからのビルド
ソースコードからのビルドにはRustツールチェインが必要です。
```sh
git clone https://github.com/erg-lang/erg.git
cd erg
cargo build --release
```
## コントリビューション
コントリビューション(プロジェクトへの貢献、協力)はいつでも歓迎しています!
何かわからないことがあれば、[discord channel]で気軽に質問してください。
## ライセンス
Ergは、MITライセンスとApache2.0ライセンスのデュアルライセンスで配布されています。
詳しくは[LICENSE-APACHE](./LICENSE-APACHE), [LICENSE-MIT](./LICENSE-MIT)をご覧ください。

99
TODO.md Normal file
View file

@ -0,0 +1,99 @@
# TODOs
* [ ] Implement the specification
* [x] Control flow
* [x] if/if!
* [x] match/match!
* [x] for!
* [x] while!
* [ ] operator
* [x] + (binary/unary)
* [x] - (binary/unary)
* [x] *
* [x] /
* [x] ** (power)
* [x] % (modulo)
* [x] comparison
* [x] ! (mutation)
* [ ] Pattern-matching
* [x] Variable Pattern
* [x] Literal Pattern
* [x] Array Pattern
* [ ] Tuple Pattern
* [ ] Record Pattern
* [ ] Data Type Pattern
* [ ] Refinement Pattern
* [x] Array literal
* [ ] Record literal
* [ ] Set literal
* [ ] Dict literal
* [ ] Tuple literal
* [x] Function definition
* [x] Procedure definition
* [ ] Type definition
* [ ] Class definition
* [ ] Trait definition
* [ ] Structural trait definition
* [ ] Patch definition
* [ ] Glue Patch definition
* [ ] Range object
* [ ] Decorator
* [ ] Comprehension
* [ ] Array
* [ ] Dict
* [ ] Set
* [ ] Tuple
* [ ] Pipeline operator
* [ ] ? operator
* [ ] Multi-line string
* [ ] String interpolation
* [ ] Multi-line comment
* [ ] Complete the type inference system
* [x] Type variable
* [x] Dependent type variable
* [ ] Polymorphic type variable
* [ ] Mutable type
* [x] Dependent mutable method
* [x] Projection type
* [ ] Polymorphic projection type
* [x] Subtyping
* [x] Refinement subtyping
* [x] Nominal subtyping
* [ ] Module system
* [ ] Recursive module
* [ ] Visibility check
* [x] Patching
* [ ] Rank-2 type
* [ ] Implement a side-effect checker
* [x] procedure call
* [ ] mutable type
* [x] Implement reference types (for methods)
* [ ] Implement a ownership checker
* [x] Implement a move checker
* [x] Implement a borrow checker
* [ ] Implement a cycle-reference detector
* [ ] Implement a compiletime evaluator
* [ ] Compiletime operator
* [ ] Compiletime function
* [ ] Maintain unit tests
* [ ] Implement a Python parser
* [ ] Make code readable
* [ ] Add docs comments to every functions
* [ ] Replace Parser (to more elegant one)
* [ ] Make error messages more readable
* [ ] Add hints (include a URL with detailed information)
* [ ] Multiple error points indication
* [ ] Support for languages other than English
* [x] Japanese
* [ ] Develop the development environment
* [ ] Implement LSP (Language Server Protocol)
* [ ] Implement a syntax highlighter (REPL/debugger built-in)
* [ ] Implement a package manager (`pack` subcommand)
* [ ] Implement a virtual environment manager (`env` subcommand)
* [ ] Prepare an installer for each platform
* [ ] Implement a compiling server
* [ ] Maintain documentations
* [ ] I18n
* [ ] Write educational materials to learn Erg while creating applications (e.g. CLI chess game -> GUI chess game, calculator -> toy language)
* [ ] Develop Dyne (CPython compatible VM)
* [ ] Develop WebAssembly backend

BIN
assets/erg_logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

1
assets/erg_logo.svg Normal file
View file

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 146.66 112.27"><defs><style>.b{fill:#040000;}.c{fill:none;}</style></defs><polygon class="c" points="10.39 108.82 30.02 108.82 44.25 57.86 24.62 57.86 10.39 108.82"/><polygon class="c" points="94.93 54.41 114.3 54.41 128.54 3.45 109.17 3.45 94.93 54.41"/><g><polygon class="c" points="10.39 108.82 30.02 108.82 44.25 57.86 24.62 57.86 10.39 108.82"/><polygon class="c" points="94.93 54.41 114.3 54.41 128.54 3.45 109.17 3.45 94.93 54.41"/><path class="b" d="M132.48,0h-34.41l-.96,3.45-14.24,50.96H30.34L44.58,3.45h34.68l.96-3.45H34.08c-2.12,0-3.97,1.41-4.54,3.45h0L15.3,54.41l-.96,3.45L.1,108.81h0c-.48,1.73,.82,3.45,2.62,3.45H48.86l.96-3.45,14.23-50.95h52.26l-14.23,50.95h-34.41l-.96,3.45h45.87c2.12,0,3.97-1.41,4.54-3.45l15.2-54.41L146.55,3.45h0c.48-1.73-.82-3.45-2.62-3.45h-11.46ZM34.77,108.81H15.14l14.23-50.95h19.63l-14.23,50.95ZM117.28,54.41h-19.37L112.14,3.45h19.37l-14.24,50.96Z"/></g></svg>

After

Width:  |  Height:  |  Size: 997 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.3 KiB

5
benchmark/bm1.er Normal file
View file

@ -0,0 +1,5 @@
fib 0 = 0
fib 1 = 1
fib n = fib(n-1) + fib(n-2)
print! fib 20

11
benchmark/bm1.js Normal file
View file

@ -0,0 +1,11 @@
function fib(n) {
if (n == 0) {
0
} else if (n == 1) {
1
} else {
fib(n - 1) + fib(n - 2)
}
}
console.log(fib(20))

6
benchmark/bm1.py Normal file
View file

@ -0,0 +1,6 @@
def fib(n):
if n == 0: return 0
elif n == 1: return 1
else: return fib(n-1) + fib(n-2)
print(fib(20))

31
doc/EN/faq_general.md Normal file
View file

@ -0,0 +1,31 @@
# Erg FAQ
This FAQ is intended for the general Erg beginner.
For individual (common) technical issues, please refer to [here](./faq_technical.md) for individual (common) technical issues, and
[Here](./dev_guide/faq_syntax.md) for more information.
## What does it mean that Erg is a Python compatible language?
~~A: Erg's executable system, EVM (Erg VirtualMachine), executes Erg bytecode, which is an extension of Python bytecode. It introduces a static typing system and other features into the Python bytecode (such as introducing arguments to instructions that do not take arguments, and implementing unique instructions in the free numbers). This allows Erg to call Python code seamlessly and execute it fast.~~
A: Erg code is transpiled into Python bytecode. That is, it runs on the same interpreter as Python. Originally, we planned to develop a Cpython-compatible interpreter, and to combine it with the compiler to form "Erg". However, since the development of the processing system has lagged far behind that of the compiler, we have decided to release only the compiler in advance (But the interpreter is still under development).
## What languages have influenced Erg?
We have been influenced by more languages than we can count on both hands, but Python, Rust, Nim, and Haskell have been the strongest influences.
We inherited many semantics from Python, expression-oriented and trait from Rust, procedures from Nim, and functional programming-related features from Haskell.
## Languages that can call Python include Julia. Why did you create Erg?
A: One of the motivations for Erg's design was to have a language that is easy to use, yet has a powerful type system. That is, a language with type inference, Kind, dependent types, etc.
Julia can be typed, but it is really a dynamically typed language and does not have the compile-time error detection benefits of statically typed languages.
## Erg supports multiple styles of programming, including functional and object-oriented programming. Isn't this contrary to Python's "There should be one --and preferably only one-- obvious way to do it."?
A: In Erg, the term is taken in a more narrow context. For example, there are generally no aliases in the Erg API; Erg is "only one way" in this context.
In a larger context, such as FP or OOP, having only one way of doing things is not necessarily a convenience.
For example, JavaScript has several libraries to help create immutable programs, and C has several libraries for garbage collection.
However, having multiple libraries for even such basic features not only takes time to select, but also creates significant difficulties in integrating code that uses different libraries.
Even in Haskell, a purely functional language, there are libraries that support OOP.
If programmers don't have some stuffs, they will create them on their own. So, we think it would be better to provide them as a standard.
This also fits with Python's "Battery included" concept.

23
doc/EN/faq_technical.md Normal file
View file

@ -0,0 +1,23 @@
# Technical FAQ
This section answers technical questions about using the Erg language. In other words, it contains questions that begin with What or Which, and questions that can be answered with Yes/No.
For more information on how the grammar was determined, see [here](./dev_guide/faq_syntax.md) for the underlying syntax decisions, and [here](./dev_guide/../faq_general.md).
## Is there an exception mechanism in Erg?
A: No. Erg uses the `Result` type instead. See [here](./dev_guide/faq_syntax.md) for why Erg does not have an exception mechanism.
## Does Erg have a type equivalent to TypeScript's `Any`?
A: No, there is not. All objects belong to at least the `Object` class, but this type only provides a minimal set of attributes, so you can't do whatever you want with it like you can with Any.
The `Object` class is converted to the desired type through dynamic inspection by `match`, etc. It is the same kind of `Object` in Java and other languages.
In the Erg world, there is no chaos and hopelessness like in TypeScript, where the API definition is ``Any''.
## What is the difference between Never, {}, None, (), NotImplemented, and Ellipsis?
A: `Never` is an "impossible" type. A subroutine that produces a runtime error has `Never` (or a merger type of `Never`) as its return type. The program will stop as soon as it detects this. Although the `Never` type is by definition also a subclass of all types, `Never` type objects never appear in Erg code and are never created. `{}` is equivalent to `Never`.
`Ellipsis` is an object that represents an ellipsis, and comes from Python.
`NotImplemented` is also from Python. It is used as a marker for not implemented, but Erg prefers the `todo` function which produces an error.
`None` is an instance of `NoneType`. It is often used with the `Option` type.
`()` is a unit type and an instance of itself. It is used when you want to return a "meaningless value" such as the return value of a procedure.

48
doc/EN/improved_points.md Normal file
View file

@ -0,0 +1,48 @@
# Improvements from Python
## Perform static analysis (static type checking, variable and property checking)
The benefit of static type checking cannot be emphasized enough now, but checking for the existence of variables and properties is also a part that comes into play quite a bit.
## Strict scope handling
In Python, statements do not have scopes.
Therefore, variables defined in a `for` or `if` have outside effects. You cannot name variables casually.
```python
for i in range(10):
x = 1
print(i + x)
print(x) # 1
```
In Erg, all blocks have scope and are completely isolated.
## Clear distinction between mutable and immutable objects
Python is not clear on the distinction between mutable and immutable / heap and value objects, so you have to keep in mind that tuples are immutable but lists are mutable... You need to keep in mind that tuples are immutable, but lists are mutable... and so on.
Also, if you want to make your own classes immutable, you have to go through a tedious process.
```python
# Can you believe this code is valid for the past versions of Python?
i = 256
assert i is 256
i = 257
assert i is not 257
```
## Traits
Just like Java's interface, you can do contract-based programming.
Python also has ABC (Abstract Base Class), but this kind of structure works best with static typing.
## Resolve dependencies statically
This prevents the annoying experience of running a program for a long time and then running it with an error due to missing modules.
## Built-in package manager
Reproducible builds with a standardized directory structure and build files.
Lock file generation and version control are of course provided.
There is no need to choice or mix anaconda, pyenv, poetry, etc. for each project.

25
doc/EN/index.md Normal file
View file

@ -0,0 +1,25 @@
# Index
## [API/](./API/index.md)
This section describes the specifications of subroutines, types, constants, etc. provided by Erg's built-in or standard libraries.
## [compiler/](./compiler/index.md)
Describes the design of the Erg compiler (Centimetre).
## [dev_guide/](./dev_guide/index.md)
It explains the development policy of the project, how to make contributions, etc.
## [python/](./python/index.md)
The knowledge of Python required to develop Erg is explained.
## [syntax/](./syntax/00_basic.md)
The syntax of Erg is explained.
## [tools/](./tools/index.md)
Explains how to use Erg's peripheral tools and command options.

View file

@ -0,0 +1,28 @@
# Tips on migrating from Python to Erg
## Want to convert a string to an int, etc
Use the `parse` method of the `Str` class. It returns a `Result` type.
```python
# Python
s: str
i: int = int(s)
```
```erg
# Erg
s: Str
res: Result(Int, IntParseError) = s.parse Int
i: Int = res.unwrap()
f: Result(Float, FloatParseError) = s.parse Float
```
You can also use the `try_from` method.
```erg
# Erg
s: Str
i: Int = Int.try_from(s).unwrap()
f: Float = Float.try_from(s).unwrap()
```

120
doc/EN/syntax/00_basic.md Normal file
View file

@ -0,0 +1,120 @@
# Basics
> __Info__: This document is incomplete. It has not been proofread (style, correct links, mistranslation, etc.). Also, Erg's syntax may be change destructively during version 0.*, and the documentation may not have been updated accordingly. Please be aware of this beforehand.
> If you find any errors in this document, please report then to [here form](https://forms.gle/HtLYRfYzWCAaeTGb6) or [GitHub repo](https://github.com/mtshiba/TheErgBook/issues/new). We would appreciate your suggestions.
>
> [The Erg book original version (Japanese)](http://mtshiba.me/TheErgBook/)
This document describes the basic syntax of Erg. The [Standard API](./API/index.md) and [internal documents for Erg contributors](./dev_guide/index.md) are located in another directory.
## Hello, World&excl;
First, let's do "Hello World".
```erg
print!("Hello, World!")
```
This is almost identical to Python and other languages in the same family. The most striking feature is the `!`, the meaning of which will be explained later.
In Erg, parentheses `()` can be omitted unless there is some confusion in interpretation.
The omission of parentheses is similar to Ruby, but it is not possible to omit parentheses that can be interpreted in more than one way.
```erg
print! "Hello, World!" # OK
print! "Hello,", "World!" # OK
print!() # OK
print! # OK, but this does not mean to call, simply to get `print!` as a callable object
print! f x # OK, interpreted as `print!(f(x))`
print!(f(x, y)) # OK
print! f(x, y) # OK
print! f(x, g y) # OK
print! f x, y # NG, can be taken to mean either `print!(f(x), y)` or `print!(f(x, y))` print!
print!(f x, y) # NG, can be taken to mean either `print!(f(x), y)` or `print!(f(x, y))`
print! f(x, g y, z) # NG, can be taken to mean either `print!(x, g(y), z)` or `print!(x, g(y, z))`
```
## Scripts
Erg code is called a script. Scripts can be saved and executed in file format (.er).
## Comments
The code after `#` is ignored as a comment. Use this to explain the intent of the code or to temporarily disable the code.
```erg
# Comment
# `#` and after are ignored until a new line is inserted
#[
Multi-line comment
Treated as a comment all the way up to the corresponding `]#`
]#
```
## Expressions, separators
A script is a series of expressions. An expression is something that can be calculated or evaluated, and in Erg almost everything is an expression.
Each expression is separated by a separator - either a new line or a semicolon `;`-.
Erg scripts are basically evaluated from left to right, top to bottom.
```erg
n = 1 # assignment expression
f(1, 2) # function-call expression
1 + 1 # operator-call expression
f(1, 2); 1 + 1
```
As shown below, there is a syntax called instant block that takes the last expression evaluated in the block as the value of the variable.
This differs from a function with no arguments, which does not add `()`. Note that instant blocks are evaluated only once on the fly.
```erg
i =
x = 1
x + 1
assert i == 2
```
This cannot be accomplished with a semicolon (`;`).
```erg
i = (x = 1; x + 1) # SyntaxError: cannot use `;` in parentheses
```
## Indentation
Erg, like Python, uses indentation to represent blocks. There are five operators (special forms) that trigger the start of a block: `=`, `->`, `=>`, `do`, and `do!` (In addition, `:` and `|`, although not operators, also produce indentation). The meanings of each are described later.
```erg
f x, y =
x + y
for! 0..9, i =>
print!
for! 0..9, i =>
print! i; print! i
ans = match x:
0 -> "zero"
_: 0..9 -> "1 dight"
_: 10..99 -> "2 dights"
_ -> "unknown"
```
If a line is too long, it can be broken using `\`.
```erg
# this does not means `x + y + z` but means `x; +y; +z`
x
+ y
+ z
# this means `x + y + z`
x \
+ y \
+ z
```
<p align='center'>
Previous | <a href='. /01_literal.md'>Next</a>
</p>

149
doc/EN/syntax/01_literal.md Normal file
View file

@ -0,0 +1,149 @@
# Literal
## Basic Literals
### Int Literal
```erg
0, -0, 1, -1, 2, -2, 3, -3, ...
```
### Ratio Literal
```erg
0.00, -0.0, 0.1, 400.104, ...
```
If a `Ratio` literal has an integer or decimal part of `0`, you can omit the `0`.
```erg
assert 1.0 == 1.
assert 0.5 == 0.5
```
> __Note__: This function `assert` was used to show that `1.0` and `1.` are equal.
Subsequent documents may use `assert` to indicate that the results are equal.
### Str Literal
Any Unicode-representable string can be used.
Unlike Python, quotation marks cannot be enclosed in `'`. If you want to use `"` in a string, use `\"`.
```erg
"", "a", "abc", "111", "1# 3f2-3*8$", "こんにちは", "السَّلَامُ عَلَيْكُمْ", ...
```
`{}` allows you to embed expressions in strings. This is called string interpolation.
If you want to output `{`, `}` itself, use `\{`, `\}`.
```erg
assert "1 + 1 is 2" == "{1} + {1} is {1+1}"
s = "1+1"
assert "\{1+1}\" == "\{{s}\}"
```
### Exponential Literal
This is a literal representing exponential notation often used in academic calculations. It is an instance of type ``Ratio``.
The notation is the same as in Python.
```erg
1e-34, 0.4e-10, 2.455+e5, 245e5, 25E5, ...
```
```erg
assert 1e-10 == 0.0000000001
```
## Compound Literals
Each of these literals has its own documentation describing them separately, so please refer to that documentation for details.
### [Array Literal](./10_array.md)
```erg
[], [1], [1, 2, 3], ["1", "2",], [1, "1", True, [1]], ...
```
### [Dict Literal](./11_dict.md)
```erg
{:}, {"one": 1}, {"one": 1, "two": 2}, {"1": 1, "2": 2}, {1: "1", 2: True, "three": [1]}, ...
```
### [Tuple Literal](./12_tuple.md)
```erg
(), (1, 2, 3), (1, "hello", True), ...
```
### [Record Literal](./13_record.md)
```erg
{=}, {one = 1}, {one = 1; two = 2}, {.name = "John"; .age = 12}, {.name = Str; .age = Nat}, ...
```
### [Set Literal](./14_set.md)
```erg
{}, {1}, {1, 2, 3}, {"1", "2", "1"}, {1, "1", True, [1]} ...
```
As a difference from `Array` literals, duplicate elements are removed in `Set`.
```erg
assert {1, 2, 1} == {1, 2}
```
### What looks like a literal but isn't
## Boolean Object
```erg
True, False
```
### None Object
```erg
None
```
## Range Object
```erg
assert 0..5 == {1, 2, 3, 4, 5}
assert 0..10 in 5
assert 0..<10 notin 10
assert 0..9 == 0..<10
```
## Float Object
```erg
assert 0.0f64 == 0
assert 0.0f32 == 0.0f64
```
Float objects are constructed by multiplying a `Ratio` object by `f64`, which is a `Float 64` unit object.
## Complex Object
```erg
1+2im, 0.4-1.2im, 0im, im
```
A `Complex` object is simply an arithmetic combination of an imaginary unit object, `im`.
## *-less multiplication
In Erg, you can omit the `*` to indicate multiplication as long as there is no confusion in interpretation. However, the combined strength of the operators is set stronger than `*`.
```erg
# same as `assert (1*m) / (1*s) == 1*(m/s)`
assert 1m / 1s == 1 (m/s)
```
<p align='center'>
<a href='. /00_basic.md'>Previous</a> | <a href='. /02_name.md'>Next</a>
</p>

164
doc/EN/syntax/02_name.md Normal file
View file

@ -0,0 +1,164 @@
# Variable
Variables are a type of algebra; algebra in Erg - sometimes simply referred to as variable if there is no confusion - refers to the feature to name objects and make them referable from elsewhere in the code.
A variable is defined as follows.
The `n` part is called the variable name (or identifier), `=` is the assignment operator, and the `1` part is the assigned value.
```erg
n = 1
```
The `n` defined in this way can thereafter be used as a variable to denote the integer object `1`. This system is called assignment (or binding).
We have just said that `1` is an object. We will discuss what an object is later, but for now we will assume that it is something that can be assigned to, i.e., on the right side of the assignment operator (`=`, etc.).
If you want to specify the "type" of a variable, do the following. The type is roughly the set to which an object belongs, as will be explained later.
Here we specify that `n` is a natural number (`Nat`) type.
```erg
n: Nat = 1
```
Note that, unlike other languages, multiple assignments are not allowed.
```erg
# NG
l1 = l2 = [1, 2, 3] # SyntaxError: multiple assignment not allowed
# OK
l1 = [1, 2, 3]
l2 = l1.clone()
```
It is also not possible to reassign to a variable. The syntax that can be used instead, to hold mutable states, are described later.
```erg
i = 1
i = i + 1 # AssignError: cannot assign twice
```
You can define a variable with the same name in the inner scope, but you are only covering it over, not destructively rewriting its value. If you go back to the outer scope, the value will return as well.
Note that this is a different behavior than the Python "statement" scope.
This kind of functionality is generally referred to as shadowing. However, unlike shadowing in other languages, you cannot shadow in the same scope.
```erg
x = 0
# x = 1 # AssignError: cannot assign twice
if x.is_zero(), do:
x = 1 # different from outer x with same name
assert x == 1
assert x == 0
```
The following may seem possible at first glance, but it is still not possible. This is a design decision, not a technical constraint.
```erg
x = 0
if x.is_zero(), do:
x = x + 1 # NameError: cannot define variables refer to variables with the same name
assert x == 1
assert x == 0
```
## Constants
Constants are also a type of algebra. If you start an identifier with a capital letter, it is treated as a constant. They are called constants because once defined, they do not change.
The `N` part is called the constant name (or identifier). Otherwise, it is the same as a variable.
```erg
N = 0
if True, do:
N = 1 # AssignError: constants cannot be shadowed
pass()
```
Constants are immutable beyond the defined scope. They cannot be shadowed. Because of this property, constants can be used in pattern matching. Pattern matching is explained later.
For example, constants are used for mathematical constants, information about external resources, and other immutable values.
It is common practice to use all-caps (style in which all letters are capitalized) for identifiers of objects other than [types](./type/01_type_system.md).
```erg
PI = 3.141592653589793
URL = "https://example.com"
CHOICES = ["a", "b", "c"]
```
```erg
PI = 3.141592653589793
match! x:
PI => print! "π"
other => print! "other"
```
The above code prints `π` when `x` is `3.141592653589793`. If `x` is changed to any other number, it prints `other`.
Some objects cannot be bound as constants. Mutable objects, for example. Mutable objects are objects whose states can be changed, as described in detail later.
This is because of the rule that only constant expressions can be assigned to constants. Constant expressions are also discussed later.
```erg
X = 1 # OK
X = !1 # TypeError: cannot define Int! object as a constant
```
## Delete an Variable
You can delete an variable by using the `Del` function. All other variables that depend on the variable (that is, that refer directly to the value of the variable) are also removed.
```erg
x = 1
y = 2
Z = 3
f a = x + a
assert f(2) == 3
Del x
Del y, Z
f(2) # NameError: f is not defined (deleted in line 6)
```
Note that `Del` can only delete variables defined in the user-defined module. Built-in constants such as `True` cannot be deleted.
```erg
Del True # TypeError: cannot delete built-in constants
Del print! # TypeError: cannot delete built-in variables
```
## Appendix: Assignment and Equivalence
Note that `x == a` is not necessarily true when `x = a`. An example is `Float.NaN`. This is the formal specification of floating-point numbers as defined by IEEE 754.
```erg
x = Float.NaN
assert x ! = NaN
assert x ! = x
```
There are other objects for which no equivalence relation is defined in the first place.
```erg
f = x -> x**2 + 2x + 1
g = x -> (x + 1)**2
f == g # TypeError: cannot compare function objects
C = Class {i: Int}
D = Class {i: Int}
C == D # TypeError: cannot compare class objects
```
Strictly speaking, `=` does not assign the right-hand side value directly to the left-hand side identifier.
In the case of function and class objects, "modification" such as giving variable name information to the object is performed. However, this is not the case for structural types.
```erg
f x = x
print! f # <function f>
g x = x + 1
print! g # <function g>
C = Class {i: Int}
print! C # <class C>
```
<p align='center'>
<a href='. /01_literal.md'>Previous</a> | <a href='. /03_declaration.md'>Next</a>
</p>

View file

@ -0,0 +1,49 @@
# Declaration
Declaration is the syntax for specifying the type of variable to be used.
Declarations can be made anywhere in the code, but declarations alone do not refer to the variables. They must be initialized.
After the assignment, the declaration can be checked to ensure that the type is compatible with the object to which it is assigned.
```erg
i: Int
# Can be declared at the same time as the assignment, like i: Int = 2
i = 2
i: Num
i: Nat
i: -2..2
i: {2}
```
Declaration after assignment is similar to type checking by `assert`, but has the feature that it is checked at compile time.
Type checking by `assert` at runtime can be checked for "may be type Foo", but type checking by `:` at compile time is strict: if the type is not determined to be "type Foo", it will not pass the check and an error will occur.
```erg
i = (-1..10).sample!
assert i in Nat # this may pass
i: Int # this will pass
i: Nat # this will not pass (-1 is not an element of Nat)
```
Functions can be declared in four different ways. None of them can omit the return type.
```erg
f(x: Int, y: Int): Int
f(Int, Int): Int
f: (x: Int, y: Int) -> Int
f: (Int, Int) -> Int
```
If you declare the argument names explicitly, a type error will result if the names are different at definition time. If you want to give the argument names arbitrary names, you can declare them in the second or fourth way. In that case, only the method name and its type will be seen by type checking.
```erg
T = Trait {
.f(x: Int, y: Int): Int
}
C = Class(U, Impl: T)
C.f(a: Int, b: Int): Int = ... # TypeError: `.f` must be type of `(x: Int, y: Int) -> Int`, not `(a: Int, b: Int) -> Int`
```
<p align='center'>
<a href='. /02_name.md'>Previous</a> | <a href='. /04_function.md'>Next</a>
</p>

View file

@ -0,0 +1,281 @@
# Function
A function is a block that takes an "argument", processes it, and returns it as a "return value". It is defined as follows.
```erg
add x, y = x + y
# or
add(x, y) = x + y
```
The names specified after a function name are called parameters.
In contrast, the objects passed to a function are called arguments.
The function `add` is a function that takes `x` and `y` as parameters and returns the sum of them, `x + y`.
The defined function can be called (applied/invoked) as follows.
```erg
add 1, 2
# or
add(1, 2)
```
## Colon application style
Functions are invoked like `f x, y, ...`, but if there are too many arguments for a single line, they can be applied using `:` (colon).
```erg
f some_long_name_variable_1 + some_long_name_variable_2, some_long_name_variable_3 * some_long_name_variable_4
```
```erg
f some_long_name_variable_1 + some_long_name_variable_2:
some_long_name_variable_3 * some_long_name_variable_4
```
```erg
f:
some_long_name_variable_1 + some_long_name_variable_2
some_long_name_variable_3 * some_long_name_variable_4
```
All three codes above mean the same thing. This style is also useful when using `if` functions, for example.
```erg
result = if Bool.sample!():
do:
log "True was chosen"
1
do:
log "False was chosen"
0
```
After `:`, no code other than comments may be written, and must always be on a new line.
## Keyword Arguments
If a function is defined with a large number of parameters, there is a danger of passing the arguments in the wrong order.
In such cases, it is safe to call the function using keyword arguments.
```erg
f x, y, z, w, v, u: Int = ...
```
The functions defined above have many arguments and are arranged in a confusing order. You should not create such a function, but you may encounter such code when using code written by others. Therefore, we use keyword arguments. If you use keyword arguments, the values are passed from the name to the correct argument, even if they are in the wrong order.
```erg
f u: 6, v: 5, w: 4, x: 1, y: 2, z: 3
```
Note that keyword arguments and a new line immediately after the `:` are considered a colon-call style.
```erg
# means `f(x: y)`
f x: y
# means `f(x, y)`
f x:
y
```
## Default parameters
Default parameters are used when some parameters are mostly fixed and you want to be able to omit them.
Default parameters are specified by `|=`(or-assign operator). If `base` is not specified, assign `math.E` to `base`.
```erg
math_log x: Ratio, base |= math.E = ...
assert math_log(100, 10) == 2
assert math_log(100) == math_log(100, math.E)
```
Note that there is a distinction between specifying no argument and assigning `None`.
```erg
p! x |= 0 = print!
p!(2) # 2
p!() # 0
p!(None) # None
```
Can also be used with type specification and patterns.
```erg
math_log x, base: Ratio |= math.E = ...
f [x, y] |= [1, 2] = ...
```
However, within the default arguments, it is not possible to call the procedures (described later) or assign mutable objects.
```erg
f x |= p! 1 = ... # NG
```
Also, the argument just defined cannot be used as the value passed to the default argument.
```erg
f x |= 1, y |= x = ... # NG
```
## Variable-length arguments
The `log` function, which outputs a log (record) of its arguments, can take any number of arguments.
```erg
log "Hello", "World", "!" # Hello World !
```
To define such a function, add `...` to a parameter. This way, the function receives arguments as variable-length tuples.
```erg
f ...x =
for x, i ->
log i
# x == [1, 2, 3, 4, 5]
f 1, 2, 3, 4, 5
```
## Function definition with multiple patterns
```erg
fib n: Nat =
match n:
0 -> 0
1 -> 1
n -> fib(n - 1) + fib(n - 2)
```
Functions like the one above, where `match` appears directly under the definition, can be rewritten as follows.
```erg
fib 0 = 0
fib 1 = 1
fib(n: Nat): Nat = fib(n - 1) + fib(n - 2)
```
Note that a function definition with multiple patterns is not so-called overloading (multiple definition); a function has only a single definition. In the example above, `n` must be of the same type as `0` or `1`. Also, as with `match`, pattern matching is done from top to bottom.
If instances of different classes are mixed, the last definition must specify that the function argument is of type `Or`.
```erg
f "aa" = ...
f 1 = ...
# `f x = ... ` is invalid
f x: Int or Str = ...
```
Also, like `match`, it must be exhaustive. For emergency, you can use `fib _ = not_implemented()`.
```erg
fib 0 = 0
fib 1 = 1
# PatternError: pattern of fib's parameter is not exhaustive
```
## Recursive functions
A recursive function is a function that includes itself in its definition.
As a simple example, let us define a function `factorial` that performs a factorial calculation. Factorial is a computation that "multiplies all positive numbers less than or equal to".
The factorial of 5 is `5*4*3*2*1 == 120`.
```erg
factorial 0 = 1
factorial 1 = 1
factorial(n: Nat): Nat = n * factorial(n - 1)
```
First, from the definition of factorial, the factorial of 0 and 1 are both 1.
In turn, the factorial of 2 is `2*1 == 2`, the factorial of 3 is `3*2*1 == 6`, and the factorial of 4 is `4*3*2*1 == 24`.
If we look closely, we can see that the factorial of a number n is the factorial of the preceding number n-1 multiplied by n.
Putting this into code, we get `n * factorial(n - 1)`.
Since the definition of `factorial` contains itself, `factorial` is a recursive function.
As a reminder, if you do not add a type specification, it is inferred like this.
```erg
factorial: |T <: Sub(Int, T) and Mul(Int, Int) and Eq(Int)| T -> Int
factorial 0 = 1
factorial 1 = 1
factorial n = n * factorial(n - 1)
```
However, even if you can reason about it, you should explicitly specify the type of the recursive function. In the example above, a code like ``factorial(-1)`` would work, but
```erg
factorial(-1) == -1 * factorial(-2) == -1 * -2 * factorial(-3) == ...
```
and this computation does not stop. Recursive functions must carefully define the range of values or you may end up in an infinite loop.
So the type specification also helps to avoid accepting unexpected values.
## Compile-time functions
A function name begins with an uppercase letter to indicate a compile-time function. User-defined compile-time functions must have all arguments as constants and must specify their types.
Compile-time functions are limited in what they can do. Only constant expressions can be used in compile-time functions, i.e., only some operators (such as quadrature, comparison, and type construction operations) and compile-time functions. Arguments to be passed must also be constant expressions.
In return, the advantage is that the computation can be done at compile time.
```erg
Add(X, Y: Nat): Nat = X + Y
assert Add(1, 2) == 3
Factorial 0 = 1
Factorial(X: Nat): Nat = X * Factorial(X - 1)
assert Factorial(10) == 3628800
math = import "math"
Sin X = math.sin X # ConstantError: this function is not computable at compile time
```
Compile-time functions are also used in polymorphic type definitions.
```erg
Option T: Type = T or NoneType
Option: Type -> Type
```
## Appendix: Function Comparison
Erg does not define `==` for functions. This is because there is no structural equivalence algorithm for functions in general.
```erg
f = x: Int -> (x + 1)**2
g = x: Int -> x**2 + 2x + 1
assert f == g # TypeError: cannot compare functions
```
Although `f` and `g` always return the same result, it is extremely difficult to make that determination. We have to teach algebra to the compiler.
So Erg gives up on function comparisons entirely, and `(x -> x) == (x -> x)` also results in a compile error. This is a different specification from Python and should be noted.
```python
# Python, weird example
f = lambda x: x
assert f == f
assert (lambda x: x) ! = (lambda x: x)
```
## Appendix2: ()-completion
```erg
f x: Object = ...
# will be completed to
f(x: Object) = ...
f a
# will be completed to
f(a)
f a, b # TypeError: f() takes 1 positional argument but 2 were given
f(a, b) # TypeError: f() takes 1 positional argument but 2 were given
f((a, b)) # OK
```
The function type `T -> U` is actually the syntax sugar of `(T,) -> U`.
<p align='center'>
<a href='. /03_declaration.md'>Previous</a> | <a href='. /05_builtin_funcs.md'>Next</a>
</p>

View file

@ -0,0 +1,49 @@
# Built-in functions
## if
`if` is a function that changes processing depending on a condition.
```erg
result: Option Int = if! Bool.sample!(), do:
log "True was chosen"
1
print! result # None (or 1)
```
`.sample!()` returns a random set of values. If the return value is true, `print! "True"` is executed.
You can also specify what to do if the condition is false; the second do block is called the else block.
```erg
result: Nat = if Bool.sample!():
do:
log "True was chosen"
1
do:
log "False was chosen"
0
print! result # 1 (or 0)
```
If the process is a single line, you can omit indentation.
```erg
result = if Bool.sample!():
do 1
do 0
```
## for
You can use `for` to write a repeating process.
```erg
match_s(ss: Iterator(Str), pat: Pattern): Option Str =
for ss, s ->
if pat.match(s).is_some():
break s
```
<p align='center'>
<a href='. /04_function.md'>Previous</a> | <a href='. /06_operator.md'>Next</a>
</p>

View file

@ -0,0 +1,29 @@
# operator
Operators are symbols that represent operations. Operands are things to the (left) right of an operator.
Operators are a kind of function, and thus are themselves first-class objects that can be bound to variables. When binding, it is necessary to enclose it with ``.
For `+` (and `-`), there are both unary and binary operators, so `_+_`(binary operation)/`+_`(unary operation ) must be specified.
``` erg
add = `+` # SyntaxError: specify `_+_` or `+_`
add=`_+_`
assert f(1, 2) == 3
assert f("a", "b") == "ab"
g = `*` # OK, this is binary only
assert g(1, 2) == 2
```
Some fundamental operators, called special forms, cannot be bound.
``` erg
def = `=` # SyntaxError: cannot bind `=` operator, this is a special form
# NG: def x, 1
function = `->` # SyntaxError: cannot bind `->` operator, this is a special form
# NG: function x, x + 1
```
<p align='center'>
<a href='./05_builtin_funcs.md'>Previous</a> | <a href='./07_side_effect.md'>Next</a>
</p>

View file

@ -0,0 +1,121 @@
# Side effects and procedures
We have been neglecting to explain the meaning of the `!`, but now its meaning will finally be revealed. This `!` indicates that this object is a "procedure" with a "side-effect". A procedure is a function with a side-effect.
```erg
f x = print! x # EffectError: functions cannot be assigned objects with side effects
# hint: change the name to 'f!'
```
The above code will result in a compile error. This is because you are using a procedure in a function. In such a case, you must define it as a procedure.
```erg
p! x = print! x
```
`p!`, `q!`, ... are typical variable names for procedures.
Procedures defined in this way also cannot be used within a function, so side-effects are completely isolated.
## Methods
Functions and procedures each can be methods. Functional methods can only take immutable references to `self`, while procedural methods can take mutable references to `self`.
The `self` is a special parameter, which in the context of a method refers to the calling object itself. The reference `self` cannot be assigned to any other variable.
```erg
C!.
method ref self =
x = self # OwnershipError: cannot move out 'self'
x
```
Procedural methods can also take [ownership]() of `self`. Remove `ref` or `ref!` from the method definition.
```erg
n = 1
s = n.into(Str) # '1'
n # ValueError: n was moved by .into (line 2)
```
Only one procedural methods can have a mutable reference at any given time. In addition, while a mutable reference is taken, no more mutable reference can be taken from the original object. In this sense, `ref!` causes a side-effect on `self`.
Note, however, that it is possible to create (immutable/mutable) references from mutable references. This allows recursion and `print!` of `self` in procedural methods.
```erg
T -> T # OK (move)
T -> Ref T # OK (move)
T => Ref! T # OK (only once)
Ref T -> T # NG
Ref T -> Ref T # OK
Ref T => Ref!
T -> Ref T # NG
T -> Ref T # OK
T => Ref!
```
## Appendix: Strict definition of side-effects
The rules for whether a code has a side-effect or not are not immediately understandable.
Until you can understand them, we recommend that you leave it to the compiler to define them as functions for the time being, and if an error occurs, add `!` to treat them as procedures.
However, for those who want to understand the exact specifications of the language, the following is a more detailed explanation of side-effects.
First, it must be stated that the equivalence of return values is irrelevant with respect to side effects in Erg.
There are procedures that for any given `x` will result in `p!(x) == p!(x)` (e.g. always return `None`), and there are functions that will result in `f(x) ! = f(x)`.
An example of the former is `print!`, and an example of the latter is the following function.
```erg
nan _ = Float.NaN
assert nan(1) ! = nan(1)
```
There are also objects, such as classes, for which equivalence determination itself is not possible.
```erg
T = Structural {i = Int}
U = Structural {i = Int}
assert T == U
C = Class {i = Int}
D = Class {i = Int}
assert C == D # TypeError: cannot compare classes
```
Back to the point: the precise definition of "side-effect" in Erg is
* Accessing mutable external information.
"External" generally refers to the outer scope; computer resources that Erg cannot touch and pre-/post-execution information are not included in "external". "Access" includes reading as well as writing.
As an example, consider the `print!` procedure. At first glance, `print!` does not seem to rewrite any variables. But if it were a function, it could rewrite outer variables, for example, with code like this:
```erg
camera = import "some_camera_module"
ocr = import "some_ocr_module"
n = 0
_ =
f x = print x # Suppose we could use print as a function
f(3.141592)
cam = camera.new() # camera faces PC display
image = cam.shot!()
n = ocr.read_num(image) # n = 3.141592
```
Think of the `camera` module as an external library providing an API for a certain camera product, and `ocr` as a library for OCR (optical character recognition).
The direct side-effect is caused by `cam.shot!()`, but obviously that information is leaked from `f`. Therefore, `print!` cannot be a function by nature.
Nevertheless, there may be cases where you want to temporarily check a value in a function and do not want to add `!` in the related function just for that purpose. In such cases, the `log` function can be used.
`log` prints the value after the entire code has been executed. In this way, side-effects are not propagated.
```erg
log "this will be printed after execution"
print! "this will be printed immediately"
# this will be printed immediately
# this will be printed after execution
```
If there is no feedback to the program, or in other words, if no external object can use the internal information, then the "leakage" of the information may be allowed. It is only necessary that the information not be "propagated".
<p align='center'>
<a href='. /06_operator.md'>Previous</a> | <a href='. /08_procedure.md'>Next</a>
</p>

View file

@ -0,0 +1,12 @@
# Procedures
Procedures are necessary when dealing with mutable objects, but having a mutable object as an argument does not necessarily make it a procedure.
Here is a function takes a mutable object (not procedure).
```erg
peek_str s: Str! = log s
```
<p align='center'>
<a href='./07_side_effect.md'>Previous</a> | <a href='./09_builtin_procs.md'>Next</a>
</p>

View file

@ -0,0 +1,14 @@
# Built-in procedure
## id!
Returns the unique identification number of the object.
Although in pure Erg semantics no difference can be found between objects with the same structure, in practice objects have different locations in memory.
`id!` returns a number representing this position.
```erg
```
<p align='center'>
<a href='. /08_procedure.md'>Previous</a> | <a href='. /10_array.md'>Next</a>
</p>

40
doc/EN/syntax/10_array.md Normal file
View file

@ -0,0 +1,40 @@
# Array
Arrays are the most basic __collection (aggregate)__.
A collection is an object that can hold multiple objects inside it.
```erg
a = [1, 2, 3]
a: [Int; 3] # Type specification: number after semicolon is the number of elements
# Can be omitted if the number of elements is not known
a: [Int]
mut_a = [!1, !2, !3]
mut_a[0].inc!()
assert mut_a == [2, 2, 3]
```
## Slice
An array can also have multiple values taken out at once. This is called slicing.
```erg
l = [1, 2, 3, 4]
# Same as l[1:3] in Python
assert l[1.. <3] == [2, 3]
assert l[1..2] == [2, 3]
# Same as l[1]
assert l[1..1] == [2]
# Same as l[::2] in Python
assert l[..].step(2) == [2, 4]
```
The object obtained by slicing is an (immutable) copy to an array.
```erg
print! Typeof l[1..2] # [Int; 4]
```
<p align='center'>
<a href='. /09_builtin_procs.md'>Previous</a> | <a href='. /11_dict.md'>Next</a>
</p>

117
doc/EN/syntax/11_tuple.md Normal file
View file

@ -0,0 +1,117 @@
# Tuple
Tuples are similar to arrays, but can hold objects of different types.
Such a collection is called an unequal collection. In contrast, homogeneous collections include arrays, sets, etc.
```erg
t = (1, True, "a")
(i, b, s) = t
assert(i == 1 and b == True and s == "a")
```
The tuple `t` can retrieve the nth element in the form `t.n`; note that unlike Python, it is not `t[n]`.
This is because accessing tuple elements is more like an attribute (the existence of the element is checked at compile time, and the type can change depending on `n`) than a method (an array's `[]` is a method).
```erg
assert t.0 == 1
assert t.1 == True
assert t.2 == "a"
```
Parentheses `()` are optional when not nested.
```erg
t = 1, True, "a"
i, b, s = t
```
Tuples can hold objects of different types, so they cannot be iterated like arrays.
```erg
t: ({1}, {2}, {3}) = (1, 2, 3)
(1, 2, 3).iter().map(x -> x + 1) # TypeError: type ({1}, {2}, {3}) has no method `.iter()`
# If all types are the same, they can be represented by `(T; n)` like arrays, but this still does not allow iteration
t: (Int; 3) = (1, 2, 3)
assert (Int; 3) == (Int, Int, Int)
```
However, nonhomogeneous collections (such as tuples) can be converted to homogeneous collections (such as arrays) by upcasting, intersecting, and so on.
This is called equalization.
```erg
(Int, Bool, Str) can be [T; 3] where T :> Int, T :> Bool, T :> Str
```
```erg
t: (Int, Bool, Str) = (1, True, "a") # non-homogenous
a: [Int or Bool or Str; 3] = [1, True, "a"] # homogenous
_a: [Show; 3] = [1, True, "a"] # homogenous
_a.iter().map(x -> log x) # OK
t.try_into([Show; 3])? .iter().map(x -> log x) # OK
```
## Unit
A tuple with zero elements is called a __unit__. A unit is a value, but also refers to its own type.
```erg
unit = ()
(): ()
```
Unit is a superclass of all element 0 tuples.
```erg
() > (Int; 0)
() > (Str; 0)
```
The use of this object is for procedures with no arguments and no return value, etc. Erg subroutines must have arguments and a return value. However, in some cases, such as a procedure, there may be no meaningful arguments or return value, only side effects. In such cases, we use units as "meaningless, formal values.
```erg
# ↓ Actually, this parenthesis is a unit
p!() =.
# `print!` does not return a meaningful value
print! "Hello, world!"
p!: () => ()
```
However, Python tends to use `None` instead of units in such cases.
In Erg, you should use `()` when you are sure from the beginning that the operation will not return a meaningful value, such as in a procedure, and return `None` when there is a possibility that the operation will fail and you will get nothing, such as when retrieving an element.
## Arguments and Tuple
Actually, all of Erg's `Callable` objects are one argument and one return value; a subroutine that takes N arguments was just receiving "one tuple with N elements" as an argument.
```erg
# f x = ... is implicitly assumed to be f(x) = ... is considered to be
f x = x
assert f(1) == 1
f(1, 2, 3) # ArgumentError: f takes 1 positional argument but 3 were given
# ArgumentError: f takes 1 positional argument but 3 were given
g x: Int, . . y: Int = y
assert (2, 3) == g 1, 2, 3
```
This also explains the function type.
```erg
assert f in T: {(T,) -> T | T}
assert g in {(Int, ... (Int; N)) -> (Int; N) | N: Nat}
```
To be precise, the function's input is not a tuple but a "Named tuple with default attributes". This is a special tuple that can only be used in function arguments, can be named like a record, and can have a default value.
```erg
f(x: Int, y=0) = x + y
f: (Int, y=Int) -> Int
f(x=0, y=1)
f(y=1, x=0)
f(x=0)
f(0)
```
<p align='center'>
<a href='. /10_array.md'>Previous</a> | <a href='. /12_dict.md'>Next</a>
</p>

67
doc/EN/syntax/12_dict.md Normal file
View file

@ -0,0 +1,67 @@
# Dict
Dict is a collection of key/value pairs.
```erg
ids = {"Alice": 145, "Bob": 214, "Charlie": 301}
assert ids["Alice"] == 145
```
The key does not have to be a string if it is a `Hash` object.
```erg
# deprecated to use a range object as a key (confused with slice)
r = {1..3: "1~3", 4..6: "4~6", 7..9: "7~9"}
assert r[1..3] == "1~3"
l = {[]: "empty", [1]: "1"}
assert l[[]] == "empty"
```
Order does not matter for Dict. It also cannot have duplicate elements. In this respect, Dict is similar to Set.
You could say that a Dict is a Set with a key.
```erg
{"Alice": 145, "Bob": 214, "Charlie": 301} == {"Alice": 145, "Charlie": 301, "Bob": 214}
```
When generating a dict from a dict literal, it is checked for duplicate keys.
Any duplicates will result in a compile error.
```erg
{"Alice": 145, "Alice": 1} # KeyError: Duplicate key "Alice"
```
Empty Dict is created with `{:}`. Note that `{}` denotes an empty set.
```erg
mut_dict = !{:}
mut_dict.insert! "Alice", 145
mut_dict.insert! "Bob", 214
assert mut_dict["Alice"] == 145
```
## Heterogeneous Dict
There need not be a single key/value type. Such a dictionary is called a __heterogenous dict_.
```erg
d: {Str: Int, Int: Str} = {"a": 1, 1: "a"}
assert d["a"] == 1
assert d[1] == "a"
```
However, it is not possible to assign values of the same type to keys of different types, or values of different types to keys of the same type.
In such cases, use the type Or instead.
```erg
invalid1 = {1: "a", "a": "b"}
invalid2 = {1: "a", 2: 2}
# Erg type inference does not infer Or type, so type specification is required
valid1: {Int or Str: Str} = {1: "a", "a": "b"}
valid2: {Int: Int or Str} = {1: "a", 2: 2}
```
<p align='center'>
<a href='./11_tuple.md'>Previous</a> | <a href='./13_record.md'>Next</a>
</p>

199
doc/EN/syntax/13_record.md Normal file
View file

@ -0,0 +1,199 @@
# Record
A record is a collection that combines the properties of a Dict accessed by key and a tuple whose access is inspected at compile time.
If you know JavaScript, think of it as a (more enhanced) kind of object literal notation.
```erg
john = {.name = "John"; .age = 21}
assert john.name == "John"
assert john.age == 21
assert john in {.name = Str; .age = Nat}
john["name"] # Error: john is not subscribable
```
The `.name` and `.age` parts are called attributes, and the `"John"` and `21` parts are called attribute values.
The difference from JavaScript object literals is that they are not accessible as strings. That is, attributes are not just strings.
This is because access to the value is determined at compile-time, and because dictionaries and records are different things. In other words, `{"name": "John"}` is a Dict and `{name = "John"}` is a record.
So how should we use dictionaries and records?
In general, we recommend using records. Records have the advantages of being checked at compile-time for the existence of elements and of being able to specify __visibility_.
Specifying visibility is equivalent to specifying public/private in Java and other languages. For details, see [visibility](. /15_visibility.md) for details.
```erg
a = {x = 1; .y = x + 1}
a.x # AttributeError: x is private
# Hint: declare as `.x`.
assert a.y == 2
```
The above example may seem strange to someone familiar with JavaScript, but simply declaring `x` makes it inaccessible from the outside. `. `. `.
You can also explicitly specify the type of an attribute.
```erg
anonymous = {
.name: Option! Str = !
.age = 20
}
anonymous.name.set! "John"
```
A record can also have the method.
```erg
o = {
.i = !0
.inc! ref! self = self.i.inc!()
}
assert o.i == 0
o.inc!()
assert o.i == 1
```
There is a notable syntax with respect to records. When all the attribute values of a record are classes (not structural types), the record itself behaves as a type with its own attributes as required attributes.
Such a type is called a record type. See the section [Record] for more details.
```erg
# record
john = {.name = "John"}
# record type
john: {.name = Str}
Named = {.name = Str}
john: Named
greet! n: Named =
print! "Hello, I am {n.name}"
john # "Hello, I am John" print!
Named.name # Str
```
## Deconstructing a record
Records can be deconstructed as follows.
```erg
record = {x = 1; y = 2}
{x = a; y = b} = record
assert a == 1
assert b == 2
point = {x = 2; y = 3; z = 4}
match point:
{x = 0; y = 0; z = 0} -> "origin"
{x = _; y = 0; z = 0} -> "on the x axis"
{x = 0; ...} -> "x = 0"
{x = x; y = y; z = z} -> "({x}, {y}, {z})"
```
`x = ...` can also be abbreviated to `x` when there is a variable with the same name as the attribute, for example, `x = x` or `x = .x` to `x`, and `.x = .x` or `.x = x` to `.x`.
However, when there is only one attribute, it must be followed by `;` to distinguish it from a set.
```erg
x = 1
y = 2
xy = {x; y}
a = 1
b = 2
ab = {.a; .b}
assert ab.a == 1
assert ab.b == 2
record = {x;}
tuple = {x}
assert tuple.1 == 1
```
This syntax can be used to deconstructed a record and assign it to a variable.
```erg
# same as `{x = x; y = y} = xy`
{x; y} = xy
assert x == 1
assert y == 2
# same as `{.a = a; .b = b} = ab`
{a; b} = ab
assert a == 1
assert b == 2
```
## Empty Record
An empty record is represented by `{=}`. An empty record is also its own class, like Unit.
```erg
empty_record = {=}
empty_record: {=}
# Object: Type = {=}
empty_record: Object
empty_record: Structural {=}
{x = 3; y = 5}: Structural {=}
```
An empty record is different from an empty Dict `{:}` or empty set `{}`. In particular, note that it is the opposite of `{}` in meaning (in Python, `{}` is an empty dictionary, while in Erg it is `!{:}` in Erg).
As an enumerated type, `{}` is an empty type that contains nothing in its elements. The `Never` type is a classification of this type.
Conversely, the record class `{=}` has no required instance attribute, so all objects are elements of it. An `Object` is an alias of this.
An `Object` (a patch of `Object`) is an element of `. __sizeof__` and other very basic provided methods.
```erg
AnyPatch = Patch Structural {=}
. __sizeof__ self = ...
.clone self = ...
...
Never = Class {}
```
Note that no other type or class can be structurally equivalent to the `{}`, `Never` type, and it is an error if the user defines a type with `{}`, `Class {}` on the right side.
This means that, for example, `1..10 or -10. -1`, but `1..10 and -10... -1`. `-1` when it should be `1..10 or -10...-1`, for example.
Also, if you define a type (such as `Int and Str`) that results in a composition `Object`, you will be warned to simply set it to `Object`.
## Instant Block
Erg has another syntax, instant block, which simply returns the last value evaluated. Attributes cannot be retained.
```erg
x =
x = 1
y = x + 1
y ** 3
assert x == 8
y =
.x = 1 # SyntaxError: cannot define an attribute in an entity block
```
## Data Class
A bare record (a record generated by a record literal) must be defined directly in the instance if you try to implement a method on its own.
This is inefficient, and as the number of attributes increases, error messages and the like become difficult to see and use.
```erg
john = {
name = "John Smith"
age = !20
.greet! ref self = print! "Hello, my name is {self::name} and I am {self::age} years old."
.inc_age! ref! self = self::age.update! x -> x + 1
}
john + 1
# TypeError: + is not implemented for {name = Str; age = Int; .greet! = Ref(Self). () => None; inc_age! = Ref! () => None}, Int
```
So, in such a case, you can inherit a record class. Such a class is called a data class.
This is described in [class](. /type/04_class.md).
```erg
Person = Inherit {name = Str; age = Nat}
Person.
greet! ref self = print! "Hello, my name is {self::name} and I am {self::age} years old."
inc_age! ref! self = self::age.update! x -> x + 1
john = Person.new {name = "John Smith"; age = 20}
john + 1
# TypeError: + is not implemented for Person, Int
```
<p align='center'>
<a href='./12_tuple.md'>Previous</a> | <a href='./14_set.md'>Next</a>
</p>

47
doc/EN/syntax/14_set.md Normal file
View file

@ -0,0 +1,47 @@
# Set
A set is an unordered array with no duplicates.
```erg
assert Set.from([1, 2, 3, 2, 1]) == {1, 2, 3}
assert {1, 2} == {1, 1, 2} # duplicates are automatically removed
assert {1, 2} == {2, 1}
```
Sets can perform mathematical set operations.
```erg
assert 1 in {1, 2, 3}
assert not 1 in {}
assert {1} or {2} == {1, 2}
assert {1, 2} and {2, 3} == {2}
assert {1, 2} not {2} == {1}
```
A set is a homogenous collection. Objects of different classes must be made equal in order to coexist.
```erg
s1 = {"a", 1, "b", -1} # TypeError
s2: {Int or Str} = {"a", 1, "b", -1}
```
## Set as Type
Sets can also be treated as types. Such a type is called an __Enum type_.
```erg
i: {1, 2, 3} = 1
assert i in {1, 2, 3}
```
The elements of the set are directly the elements of the type.
Note that the set itself is different.
```erg
mut_set = {1, 2, 3}.into {Int; !3}
mut_set.insert!(4)
```
<p align='center'>
<a href='. /13_record.md'>Previous</a> | <a href='. /15_type.md'>Next</a>
</p>

7
doc/EN/syntax/15_type.md Normal file
View file

@ -0,0 +1,7 @@
# types
Types are a very important feature in Erg, so we have a [dedicated section](./type/01_type_system.md). Please see there.
<p align='center'>
<a href='./14_set.md'>Previous</a> | <a href='./16_iterator.md'>Next</a>
</p>

View file

@ -0,0 +1,227 @@
# Erg's Type System
The following is a brief description of Erg's type system. Details are explained in other sections.
## How to define
One of the unique features of Erg is that there is not much difference in syntax between (normal) variable, function (subroutine), and type (Kind) definitions. All are defined according to the syntax of normal variable and function definitions.
```erg
f i: Int = i + 1
f # <function f>
f(1) # 2
f.method self = ... # SyntaxError: cannot define a method to a subroutine
T I: Int = {...}
T # <kind 'T'>
T(1) # Type T(1)
T.method self = ...
D = Class {private = Int; .public = Int}
D # <class 'D'>
o1 = {private = 1; .public = 2} # o1 is an object that does not belong to any class
o2 = D.new {private = 1; .public = 2} # o2 is an instance of D
o2 = D.new {.public = 2} # InitializationError: class 'D' requires attribute 'private'(: Int) but not defined
```
## Classification
All objects in Erg are strongly typed.
The top-level type is `{=}`, which implements `__repr__`, `__hash__`, `clone`, etc. (not required methods, and these attributes cannot be overridden).
Erg's type system incorporates structural subtyping (SST). The types typed by this system are called Structural types.
There are three major types of structural types: Attributive (attribute type), Refinement (sieve type), and Algebraic (algebraic type).
| Record | Enum | Interval | Union | Intersection | Diff |
| --------- | ----------- | ---------- | -------------- | ----------- | ------------ | ------------ |
| kind | Attributive | Refinement | Refinement | Algebraic | Algebraic | Algebraic | Algebraic | Algebraic
| generator | record | set | range operator | or operator | and operator | not operator
Nominal subtyping (NST) can also be used, and the conversion of an SST type to an NST type is called nominalization of the type. The resulting type is called a nominal type.
In Erg, the nominal types are classes and traits. When we simply say class/trait, we often mean record class/trait.
| Type | Abstraction | Subtyping procedure |
| --- | -------------- | ---------------- | ------------------- |
| NST | NominalType | Trait | Inheritance |
| SST | StructuralType | Structural Trait | (Implicit)
The type for the entire nominal type (`NominalType`) and the type for the entire structural type (`StructuralType`) are subtypes of the type for the entire type (`Type`).
Erg can pass arguments (type arguments) to the type definition. An `Option`, `Array`, etc. with type arguments are called a polynomial kind. These are not themselves types, but they become types by applying arguments. Types such as `Int`, `Str`, etc., which have no arguments, are called simple types (scalar types).
A type can be regarded as a set, and there is an inclusion relation. For example, `Num` contains `Add`, `Sub`, etc., and `Int` contains `Nat`.
The upper class of all classes is `Object == Class {:}` and the lower class of all types is `Never == Class {}`. This is described below.
## Types
A type like `Array T` can be regarded as a function of type `Type -> Type` that takes type `T` as an argument and returns type `Array T` (also called Kind in type theory). Types like `Array T` are specifically called polymorphic types, and `Array` itself is called unary Kind.
The type of a function whose argument and return types are known is denoted as `(T, U) -> V`. If you want to specify an entire two-argument function of the same type, you can use `|T| (T, T) -> T`, and if you want to specify an entire N-argument function, you can use `Func N`. However, the `Func N` type has no information about the number of arguments or their types, so all return values are of type `Obj` when called.
The `Proc` type is denoted as `() => Int` and so on. Also, the name of the `Proc` type instance must end with `!` at the end.
A `Method` type is a function/procedure whose first argument is the object `self` to which it belongs (by reference). For dependent types, you can also specify the type of yourself after the method is applied. This is `T!(!N)` type and `T!(N ~> N-1). () => Int` and so on.
Erg's array (Array) is what Python calls a list. `[Int; 3]` is an array class that contains three objects of type `Int`.
> __Note__: `(Type; N)` is both a type and a value, so it can be used like this.
>
> ```erg.
> Types = (Int, Str, Bool)
>
> for! Types, T =>
> print! T
> # Int Str Bool
> a: Types = (1, "aaa", True)
> ```
```erg
pop|T, N|(l: [T; N]): ([T; N-1], T) =
[...l, last] = l
(l, last)
lpop|T, N|(l: [T; N]): (T, [T; N-1]) =
[first, ...l] = l
(first, l)
```
A type ends with `!` can be rewritten internal structure. For example, the `[T; !N]` class is a dynamic array.
To create an object of type `T!` from an object of type `T`, use the unary operator `!`.
```erg
i: Int! = !1
i.update! i -> i + 1
assert i == 2
arr = [1, 2, 3]
arr.push! 4 # ImplError:
mut_arr = [1, 2, 3].into [Int; !3]
mut_arr.push4
assert mut_arr == [1, 2, 3, 4].
```
## Type Definitions
Types are defined as follows.
```erg
Point2D = {.x = Int; .y = Int}
```
Note that if `.` is omitted from a variable, it becomes a private variable used within the type. However, this is also a required attribute.
Since types are also objects, there are attributes on the types themselves. Such attributes are called type attributes. In the case of a class, they are also called class attributes.
## Data type
As mentioned earlier, a "type" in Erg roughly means a set of objects.
The following is a definition of the `Add` type, which requires `+` (the middle operator). `R, O` are the so-called type parameters, which can be a true type (class) such as `Int` or `Str`. In other languages, type parameters are given a special notation (generics, templates, etc.), but in Erg they can be defined just like normal parameters.
Type parameters can also be used for types other than type objects. For example, the array type `[Int; 3]` is a syntax sugar for `Array Int, 3`. If the type implementations overlap, the user must explicitly choose one.
```erg
Add R, O = Trait {
. `_+_` = Self.(R) -> O
}
```
.`_+_` is an abbreviation for Add.`_+_`. The prefix operator .`+_` is a method of type `Num`.
```erg
Num = Add and Sub and Mul and Eq
NumImpl = Patch Num
NumImpl.
`+_`(self): Self = self
...
```
Polymorphic types can be treated like functions. They can be monomorphic by specifying them as `Mul Int, Str`, etc. (in many cases, they are inferred with real arguments without specifying them).
```erg
1 + 1
`_+_` 1, 1
Nat.`_+_` 1, 1
Int.`_+_` 1, 1
```
The top four lines return the same result (to be exact, the bottom one returns `Int`), but it is common to use the top one.
```Ratio.`_+_`(1, 1)``` will return `2.0` without error.
This is because `Int <: Ratio`, so `1` is downcast to `Ratio`.
But this is not cast.
```erg
i = 1
if i: # TypeError: i: Int cannot be cast to Bool, use Int.is_zero() instead.
log "a"
log "b"
```
This is because `Bool <: Int` (`True == 1`, `False == 0`). Casts to subtypes generally require validation.
## Type Inference System
Erg uses static duck typing, so there is little need to explicitly specify the type.
```erg
f x, y = x + y
```
In the case of the code above, the type with `+`, i.e., `Add` is automatically inferred; Erg first infers the smallest type. If `f 0, 1`, it will infer `f x: {0}, y: {1}`, if `n: Nat; f n, 1`, it will infer `f x: Nat, y: {1}`. After minimization, the type is increased until an implementation is found. In the case of `{0}, {1}`, `Nat` is monomorphic to `Nat` since `Nat` is the smallest type with a `+` implementation.
If `{0}, {-1}`, it is monomorphic to `Int` since it does not match `Nat`. If there is no relationship between subtypes and supertypes, the one with the lowest concentration (number of instances) (or even fewer arguments in the case of polymorphic types) is tried first.
`{0}` and `{1}` are enumerated types that are partial types such as `Int` and `Nat`.
Enumerated types, for example, can be given names and request/implementation methods. In namespaces that have access to that type, objects that satisfy the request can use the implementation method.
```erg
Binary = Patch {0, 1}
Binary.
# self contains an instance. In this example, either 0 or 1.
# If you want to rewrite self, you must append ! must be added to the type name and method name.
is_zero(self) = match self:
0 -> True
1 -> False # You can also use _ -> False
is_one(self) = not self.is_zero()
to_bool(self) = match self:
0 -> False
1 -> True
```
Thereafter, the code `0.to_bool()` is possible (although `0 as Bool == False` is defined built-in).
Here is an example of a type that can actually rewrite `self` as shown in the code.
```erg
Binary! = Patch {0, 1}!
Binary!
switch! ref! self = match! self:
0 => self = 1
1 => self = 0
b = !1
b.switch!()
print! b # => 0
```
## Structure type (anonymous type)
```erg
Binary = {0, 1}
```
`Binary` in the above code is a type whose elements are `0` and `1`. It is also a subtype of the `Int` type, which has both `0` and `1`.
An object like `{}` is itself a type and can be used with or without assignment to a variable as above.
Such types are called structural types. When we want to emphasize its use as the latter in contrast to a class (named type), it is also called an unnamed type. A structural type such as `{0, 1}` is called an enumerated type, and there are also interval types, record types, and so on.
### Type Identity
The following cannot be specified. For example, you cannot specify `Int` and `Int` and `Int` and `Int` and `Int` and `Int`.
For example, `Int` and `Str` are both `Add`, but `Int` and `Str` cannot be added.
```erg
add l: Add, r: Add =
l + r # TypeError: there is no implementation of `_+_`: |T, U <: Add| (T, U) -> <Failure>
```
Also, the types `A` and `B` below are not considered the same type. However, the type `O` is considered to match.
```erg
... |R1; R2; O; A <: Add(R1, O); B <: Add(R2, O)|
```
<p align='center'>
Previous | <a href='. /02_basic.md'>Next</a>
</p>

View file

@ -0,0 +1,154 @@
# Basic syntax for types
## Type specification
In Erg, the type of a variable can be specified after `:` as follows. This can be done at the same time as an assignment.
```erg
i: Int # Declare the variable i to be of type Int
i: Int = 1
j = 1 # type specification can be omitted
```
For simple variable assignments, most type specifications can be omitted.
Type specifications are more useful when defining subroutines and types.
```erg
# Type specification for parameters
f x, y: Array Int = ...
T X, Y: Array Int = ...
```
Note that in the above case, `x, y` are both `Array Int`.
```erg
# The value of a capital variable must be a constant expression
f X: Int = X
```
Alternatively, if you don't need complete information about the type argument, you can omit it with `_`.
```erg
g v: [T; _] = ...
```
Note, however, `_` at a type specification implies `Object`.
```erg
f x: _, y: Int = x + y # TypeError: + is not implemented between Object and Int
```
## Subtype specification
In addition to the `:` (type declaration operator), Erg also allows you to specify the relationship between types by using `<:` (partial type declaration operator).
The left side of `<:` can only specify a class. Use `Subtypeof` or similar operators to compare structural types.
This is also often used when defining subroutines or types, rather than simply specifying variables.
```erg
# Subtype specification of an argument
f X <: T = ...
# Subtype specification of the required attribute (.Iterator attribute is required to be a subtype of type Iterator)
Iterable T = Trait {
.Iterator = {Iterator} # {Iterator} == {I: Type | I <: Iterator}
.iter = Self.() -> Self.Iterator T
...
}
```
You can also use a subtype specification when defining a class to statically check whether the class is a subtype of the specified type.
```erg
# Class C is a subtype of Show
C = Class Object, Impl: Show
C.show self = ... # Show's required attributes.
```
You can also specify a subtype only in specific cases.
```erg
K T: Eq
K Int <: Show and Eq
K T = Class Object
K(T).
`==` self, other = ...
K(Int).
show self = ...
```
Subtype specification is recommended when implementing structural types.
This is because, due to the nature of structural subtyping, typo or type specification errors will not cause errors when implementing required attributes.
```erg
C = Class Object
C.shoe self = ... # Show is not implemented due to Typo (it is considered just a unique method).
```
## Attribute definitions
Attributes can be defined for traits and classes only in modules.
```erg
C = Class()
C.pub_attr = "this is public"
C::private_attr = "this is private"
c = C.new()
assert c.pub_attr == "this is public"
```
The syntax for defining a batch definition is called a batch definition, in which a newline is added after `C.` or `C::` and the definitions are grouped together below the indentation.
```erg
C = Class()
C.pub1 = ...
C.pub2 = ...
C::priv1 = ...
C::priv2 = ...
# is equivalent to
C = Class()
C.
pub1 = ...
C. pub2 = ...
C::
priv1 = ...
priv2 = ...
```
## Aliasing
Types can be aliased. This allows long types, such as record types, to be shortened.
```erg
Id = Int
Point3D = {x = Int; y = Int; z = Int}
IorS = Int or Str
Vector = Array Int
```
Also, when displaying errors, the compiler will use aliases for composite types (in the above example, right-hand-side types other than the first) if they are defined.
However, only one alias of the same type is allowed per module, and multiple aliases will result in a warning.
This means that types with different purposes should be defined as separate types.
The purpose is also to prevent adding aliases on top of types that already have aliases.
```erg
Id = Int
UserId = Int # TypeWarning: duplicate aliases: Id and UserId
Ids = Array Id
Ints = Array Int # TypeWarning: duplicate aliases: Isd and Ints
IorS = Int or Str
IorSorB = IorS or Bool
IorSorB_ = Int or Str or Bool # TypeWarning: duplicate aliases: IorSorB and IorSorB_
Point2D = {x = Int; y = Int}
Point3D = {.... Point2D; z = Int}
Point = {x = Int; y = Int; z = Int} # TypeWarning: duplicate aliases: Point3D and Point
```
<p align='center'>
<a href='./01_type_system.md'>Previous</a> | <a href='./03_trait.md'>Next</a>
</p>

View file

@ -0,0 +1,150 @@
# Trait
Trait is a nominal type that adds a type attribute requirement to record types.
It is similar to the Abstract Base Class (ABC) in Python, but with the distinction of being able to perform algebraic operations.
```erg
Norm = Trait {.x = Int; .y = Int; .norm = Self.() -> Int}
```
Trait does not distinguish between attributes and methods.
Note that traits can only be declared, not implemented (implementation is achieved by a feature called patching, which will be discussed later).
Traits can be checked for implementation in a class by specifying a partial type.
```erg
Point2D <: Norm
Point2D = Class {.x = Int; .y = Int}
Point2D.norm self = self.x**2 + self.y**2
```
Error if the required attributes are not implemented.
```erg
Point2D <: Norm # TypeError: Point2D is not a subtype of Norm
Point2D = Class {.x = Int; .y = Int}
```
Traits, like structural types, can apply operations such as composition, substitution, and elimination (e.g. `T and U`). The resulting trace is called an instant trace.
```erg
T = Trait {.x = Int}
U = Trait {.y = Int}
V = Trait {.x = Int; y: Int}
assert Structural(T and U) == Structural V
assert Structural(V not U) == Structural T
W = Trait {.x = Ratio}
assert Structural(W) ! = Structural(T)
assert Structural(W) == Structural(T.replace {.x = Ratio})
```
Trait is also a type, so it can be used for normal type specification.
```erg
points: [Norm; 2] = [Point2D::new(1, 2), Point2D::new(3, 4)]
assert points.iter().map(x -> x.norm()).collect(Array) == [5, 25].
```
## Trait inclusion
The expansion operator `...` allows you to define a trace that contains a certain trace as a supertype. This is called the __subsumption__ of a trace.
In the example below, `BinAddSub` subsumes `BinAdd` and `BinSub`.
This corresponds to Inheritance in a class, but unlike Inheritance, multiple base types can be combined using `and`. Traits that are partially excluded by `not` are also allowed.
```erg
Add R, O = Trait {
. `_+_` = Self.(R) -> O
}
BinAdd = Subsume Add(Self, Self.AddO), {
.AddO = Type
}
Sub R, O = Trait {
. `_-_` = Self.(R) -> O
}
BinSub = Subsume Sub(Self, Self.SubO), {
.SubO = Type
}
BinAddSub = Subsume BinAdd(Self, Self.AddO) and BinSub(Self, Self.SubO)
```
## Structural Traits
Traits can be structured.
```erg
SAdd = Structural Trait {
. `_+_` = Self.(Self) -> Self
}
# |A <: SAdd| cannot be omitted
add|A <: SAdd| x, y: A = x.`_+_` y
C = Class {i = Int}
C.
new i = Self.__new__ {i;}
`_+_` self, other: Self = Self.new {i = self::i + other::i}
assert add(C.new(1), C.new(2)) == C.new(3)
```
Nominal traits cannot be used simply by implementing a request method, but must be explicitly declared to have been implemented.
In the following example, `add` cannot be used with an argument of type `C` because there is no explicit declaration of implementation. It must be `C = Class {i = Int}, Impl: Add`.
```erg
Add = Trait {
. `_+_` = Self.(Self) -> Self.
}
# |A <: Add| can be omitted
add|A <: Add| x, y: A = x.`_+_` y
C = Class {i = Int}
C.
new i = Self.__new__ {i;}
`_+_` self, other: Self = Self.new {i = self::i + other::i}
add C.new(1), C.new(2) # TypeError: C is not a subclass of Add
# hint: inherit or patch 'Add'
```
Structural traits do not need to be declared for this implementation, but instead type inference does not work. Type specification is required for use.
## Polymorphic Traits
Traits can take parameters. This is the same as for polymorphic types.
```erg
Mapper T: Type = Trait {
.mapIter = {Iterator}
.map = Self(T). (T -> U) -> Self.MapIter U
}
# ArrayIterator <: Mapper
# ArrayIterator.MapIter == ArrayMapper
# [1, 2, 3].iter(): ArrayIterator Int
# [1, 2, 3].iter().map(x -> "{x}"): ArrayMapper Str
assert [1, 2, 3].iter().map(x -> "{x}").collect(Array) == ["1", "2", "3"].
```
## Override in Trait
Derived traits can override the type definitions of the base trait.
In this case, the type of the overriding method must be a subtype of the base method type.
```erg
# `Self.(R) -> O` is a subtype of ``Self.(R) -> O or Panic
Div R, O: Type = Trait {
. `/` = Self.(R) -> O or Panic
}
SafeDiv R, O = Subsume Div, {
@Override
. `/` = Self.(R) -> O
}
```
## Appendix: Differences from Rust traits
Erg's trait is faithful to the one proposed by [Schärli et al.](https://www.ptidej.net/courses/ift6251/fall06/presentations/061122/061122.doc.pdf).
In order to allow algebraic operations, traits are designed to be unable to have method implementations directory, but can be patched if necessary.
<p align='center'>
<a href='./02_basic.md'>Previous</a> | <a href='./04_class.md'>Next</a>
</p>

View file

@ -0,0 +1,276 @@
# Class
A class in Erg is roughly a type that can create its own elements (instances).
Here is an example of a simple class.
```erg
Person = Class {.name = Str; .age = Nat}
Person.
new name, str = Self::__new__ {.name = name; .age = age}
john = Person.new "John Smith", 25
print! john # <Person object>
print! classof(john) # Person
```
The type given to `Class` is called the requirement type (in this case `{.name = Str; .age = Nat}`).
Instances can be created with `<Class name>::__new__ {<attribute name> = <value>; ...}` can be created with.
`{.name = "John Smith"; .age = 25}` is just a record, but it is converted to a `Person` instance by passing `Person.new`.
The subroutine that creates such an instance is called a constructor.
In the class above, the `.new` method is defined so that field names, etc. can be omitted.
## Instance and class attributes
In Python and other languages, instance attributes are often defined on the block side as follows, but note that such writing has a different meaning in Erg.
```python
# Python
class Person:
name: str
age: int
```
```erg
# In Erg, this notation implies the declaration of a class attribute (not an instance attribute)
Person = Class()
Person.
name: Str
age: Int
```
```erg
# Erg code for the Python code above
Person = Class {
.name = Str
.age = Nat
}
```
Element attributes (attributes defined in a record) and type attributes (also called instance/class attributes, especially in the case of classes) are completely different things. Type attributes are attributes of the type itself. An element of a type refers to a type attribute when it does not have the desired attribute in itself. An element attribute is a unique attribute directly possessed by the element.
Why is this distinction made? If all attributes were element attributes, it would be inefficient to duplicate and initialize all attributes when the object is created.
In addition, dividing the attributes in this way clarifies roles such as "this attribute is shared" and "this attribute is held separately".
The example below illustrates this. The attribute `species` is common to all instances, so it is more natural to use it as a class attribute. However, the attribute `name` should be an instance attribute because each instance should have it individually.
```erg
Person = Class {name = Str}
Person::
species = "human"
Person.
describe() =
log "species: {species}"
greet self =
log "Hello, My name is {self::name}."
Person.describe() # species: human
Person.greet() # TypeError: unbound method Person.greet needs an argument
john = Person.new {name = "John"}
john.describe() # species: human
john.greet() # Hello, My name is John.
alice = Person.new {name = "Alice"}
alice.describe() # species: human
alice.greet() # Hello, My name is Alice.
```
Incidentally, if an instance attribute and a type attribute have the same name and the same type, a compile error occurs. This is to avoid confusion.
```erg
C = Class {.i = Int}
C.i = 1 # AttributeError: `.i` is already defined in instance fields
```
## Class, Type
Note that the class and type of `1` are different.
There is only one class `Int` that is the generator of `1`. You can get the class to which an object belongs by `classof(obj)` or `obj.__class__`.
In contrast, there are countless types of `1`. For example, `{1}, {0, 1}, 0..12, Nat, Int, Num`.
However, the smallest type can be defined as a single type, in this case `{1}`. The type to which an object belongs can be obtained with `Typeof(obj)`. This is a compile-time function.
Objects can use patch methods as well as class methods.
Erg does not allow you to add class methods, but you can use [patch](./07_patch.md) to extend a class.
You can also inherit from existing classes ([Inheritable](./../27_decorator.md/#inheritable) class).
You can create an inherited class by using `Inherit`. The type on the left-hand side is called the derived class, and the argument type of `Inherit` on the right-hand side is called the base class (inherited class).
```erg
MyStr = Inherit Str
# other: You can use MyStr if you set ``other: Str''.
MyStr.`-` self, other: Str = self.replace other, ""
abc = MyStr.new("abc")
# Comparison here gets an upcast
assert abc - "b" == "ac"
```
Unlike Python, the defined Erg classes are `final` (non-inheritable) by default.
To make a class inheritable, an `Inheritable` decorator must be attached to the class.
Str` is one of the inheritable classes.
```erg
MyStr = Inherit Str # OK
MyStr2 = Inherit MyStr # NG
@Inheritable
InheritableMyStr = Inherit Str
MyStr3 = Inherit InheritableMyStr # OK
```
`Inherit Object` and `Class()` are almost equivalent in practice. The latter is generally used.
Classes have a different equivalence checking mechanism than types.
Types are equivalence tested based on their structure.
```erg
Person = {.name = Str; .age = Nat}
Human = {.name = Str; .age = Nat}
assert Person == Human
```
class has no equivalence relation defined.
```erg
Person = Class {.name = Str; .age = Nat}
Human = Class {.name = Str; .age = Nat}
Person == Human # TypeError: cannot compare classes
```
## Difference from structural types
We said that a class is a type that can generate its own elements, but that is not a strict description. In fact, a record type + patch can do the same thing.
```erg
Person = {.name = Str; .age = Nat}
PersonImpl = Patch Person
PersonImpl.
new name, age = {.name; .age}
john = Person.new("John Smith", 25)
```
There are four advantages to using classes.
The first is that the constructor is validity checked, the second is that it is more performant, the third is that you can use notational subtypes (NSTs), and the fourth is that you can inherit and override.
We saw earlier that record type + patch can also define a constructor (of sorts), but this is of course not a legitimate constructor. This is of course not a legitimate constructor, because it can return a completely unrelated object even if it calls itself `.new`. In the case of a class, `.new` is statically checked to see if it produces an object that satisfies the requirements.
~
Type checking for classes is simply a matter of checking the object's `. __class__` attribute of the object. So it is fast to check if an object belongs to a type.
~
Erg enables NSTs in classes; the advantages of NSTs include robustness.
When writing large programs, it is often the case that the structure of an object is coincidentally matched.
```erg
Dog = {.name = Str; .age = Nat}
DogImpl = Patch Dog
DogImpl.bark = log "Yelp!"
...
Person = {.name = Str; .age = Nat}
PersonImpl = Patch Person
PersonImpl.greet self = log "Hello, my name is {self.name}."
john = {.name = "John Smith"; .age = 20}
john.bark() # "Yelp!"
```
The structure of `Dog` and `Person` is exactly the same, but it is obviously nonsense to allow animals to greet and humans to bark.
The former is impossible, so it is safer to make it inapplicable. In such cases, it is better to use classes.
```erg
Dog = Class {.name = Str; .age = Nat}
Dog.bark = log "Yelp!"
...
Person = Class {.name = Str; .age = Nat}
Person.greet self = log "Hello, my name is {self.name}."
john = Person.new {.name = "John Smith"; .age = 20}
john.bark() # TypeError: `Person` object has no method `.bark`.
```
Another feature is that the type attributes added by the patch are virtual and are not held as entities by the implementing class.
That is, `T.x`, `T.bar` are objects that can be accessed (compile-time bound) by types compatible with `{i = Int}`, and are not defined in `{i = Int}` or `C`.
In contrast, class attributes are held by the class itself. Therefore, they cannot be accessed by classes that are not in an inheritance relationship, even if they have the same structure.
```erg
C = Class {i = Int}
C.
foo self = ...
print! dir(C) # ["foo", ...].
T = Patch {i = Int}
T.
x = 1
bar self = ...
print! dir(T) # ["bar", "x", ...].
assert T.x == 1
assert {i = 1}.x == 1
print! T.bar # <function bar>
{i = Int}.bar # TypeError: Record({i = Int}) has no method `.bar`.
C.bar # TypeError: C has no method `.bar` print!
print! {i = 1}.bar # <method bar>
C.new({i = 1}).bar # <method bar>
```
## Difference from data class
There are two types of classes: regular classes, which are record classes through `Class`, and data classes, which inherit (`Inherit`) from record classes.
The data class inherits the functionality of the record class and has features such as decomposition assignment, `==` and `hash` implemented by default, etc. On the other hand, the data class has its own equivalence relation and format display.
On the other hand, if you want to define your own equivalence relations or formatting displays, you should use the normal class.
```erg
C = Class {i = Int}
c = C.new {i = 1}
d = C.new {i = 2}
print! c # <C object>
c == d # TypeError: `==` is not implemented for `C`
D = Inherit {i = Int}
e = D.new {i = 1}
f = D.new {i = 2}
print! e # D{i = 1}
assert e ! = f
```
## Enum Class
To facilitate defining classes of type `Or`, an `Enum` is provided.
```erg
X = Class()
Y = Class()
XorY = Enum X, Y
```
Each type can be accessed as `XorY.X`, `XorY.Y` and the constructor can be obtained as `XorY.cons(X)`.
`.cons` is a method that takes a class and returns its constructor.
```erg
x1 = XorY.new X.new()
x2 = XorY.cons(X)()
assert x1 == x2
```
## Class Relationships
A class is a subtype of a requirement type. methods (including patch methods) of the requirement type can be used in the class.
```erg
T = Trait ...
T.foo: Foo
C = Class(... , impl: T)
C.
foo = foo
bar x = ...
assert C < T
assert C.foo == foo
assert not T < C
T.foo # AttributeError
```
<p align='center'>
<a href='. /03_trait.md'>Previous</a> | <a href='. /05_inheritance.md'>Next</a>
</p>

View file

@ -0,0 +1,253 @@
# Inheritance
Inheritance allows you to define a new class that adds functionality or specialization to an existing class.
Inheritance is similar to inclusion in a trace. The inherited class becomes a subtype of the original class.
```erg
NewInt = Inherit Int
NewInt.plus1 self = self + 1
assert NewInt.new(1).plus1() == 2
assert NewInt.new(1) + NewInt.new(1) == 2
```
If you want the newly defined class to be inheritable, you must give it the `Inheritable` decorator.
You can specify an optional argument `additional` to allow the class to have additional instance attributes, but only if the class is a value class. However, you cannot add instance attributes if the class is a value class.
```erg
@Inheritable
Person = Class {name = Str}
Student = Inherit Person, additional: {id = Int}
john = Person.new {name = "John"}
alice = Student.new {name = "Alice", id = 123}
MailAddress = Inherit Str, additional: {owner = Str} # TypeError: instance variables cannot be added to a value class
```
Erg is exceptionally designed not to allow inheritance of type ``Never``. Erg is exceptionally designed not to allow inheritance of `Never` type, because `Never` is a unique class that can never be instantiated.
## Inheritance of Enumerated Classes
[Or type](./13_algebraic.md) can also be inherited. In this case, you can remove any of the choices (multiple choices are possible with `or`) by specifying the optional argument `Excluding`.
No additional choices can be added. The class to which you add an option is not a subtype of the original class.
```erg
Number = Class Int or Float or Complex
Number.abs(self): Float =
match self:
i: Int -> i.abs().into Float
f: Float -> f.abs()
c: Complex -> c.abs().into Float
# c: Complex cannot appear in match choices
RealNumber = Inherit Number, Excluding: Complex
```
Similarly, [sieve type](. /12_refinement.md) can also be specified.
```erg
Months = Class 0..12
MonthsNot31Days = Inherit Months, Excluding: {1, 3, 5, 7, 8, 10, 12}
StrMoreThan3 = Class StrWithLen N | N >= 3
StrMoreThan4 = Inherit StrMoreThan3, Excluding: StrWithLen N | N == 3
```
## Overriding
The class is the same as the patch in that new methods can be added to the original type, but the class can be further "overridden".
This overriding is called override. To override, three conditions must be met.
First, the override must have an `Override` decorator because by default it will result in an error.
In addition, the override cannot change the type of the method. It must be a subtype of the original type.
And if you override a method that is referenced by another method, you must also override all referenced methods.
Why is this condition necessary? It is because overriding does not merely change the behavior of one method, but may affect the behavior of another method.
Let's start with the first condition. This condition is to prevent "accidental overrides.
In other words, the `Override` decorator must be used to prevent the name of a newly defined method in a derived class from conflicting with the name of the base class.
Next, consider the second condition. This is for type consistency. Since the derived class is a subtype of the base class, its behavior must also be compatible with that of the base class.
Finally, consider the third condition. This condition is unique to Erg and not often found in other object-oriented languages, again for safety. Let's look at what could go wrong if this were not the case.
```erg
# Bad example
@Inheritable
Base! = Class {x = Int!}
Base!
f! ref! self =
print! self::x
self.g!()
g! ref! self = self::x.update! x -> x + 1
Inherited! = Inherit Base!
Inherited!
@Override
g! ref! self = self.f!() # InfiniteRecursionWarning: This code falls into an infinite loop
# OverrideError: method `.g` is referenced by `.f` but not overridden
```
In the inherited class `Inherited!`, the `.g!` method is overridden to transfer processing to `.f!`. However, the `.f!` method in the base class transfers its processing to `.g!`, resulting in an infinite loop. `.f` was a problem-free method in the `Base!` class, but it was used in an unexpected way by the override, and it was broken.
Erg has built this rule into the specification.
```erg
# OK.
@Inheritable
Base! = Class {x = Int!}
Base!
f! ref! self =
print! self::x
self.g!()
g! ref! self = self::x.update! x -> x + 1
Inherited! = Inherit Base!
Inherited!
@Override
f! ref! self =
print! self::x
self::x.update! x -> x + 1
@Override
g! ref! self = self.f!()
```
However, this specification does not completely solve the override problem. However, this specification does not completely solve the override problem, since the compiler cannot detect if the override fixes the problem.
It is the responsibility of the programmer creating the derived class to correct the effects of the override. Whenever possible, try to define an alias method.
### Replacing Traits (or what looks like it)
Although it is not possible to replace traits at inheritance time, there are examples that appear to do so.
For example, `Int`, a subtype of `Real` (which implements `Add()`), appears to reimplement `Add()`.
```erg
Int = Class ... , Impl: Add() and ...
```
But in fact `Add()` in `Real` stands for `Add(Real, Real)`, and in `Int` it is just overwritten by `Add(Int, Int)`.
They are two different traits (`Add` is a [covariate](. /advanced/variance.md), so `Add(Real, Real) :> Add(Int, Int)`).
## Multiple Inheritance
Erg does not allow intersection, diff, and complement between normal classes.
```erg
Int and Str # TypeError: cannot unite classes
```
This rule prevents inheritance from multiple classes, i.e., multiple inheritance.
```erg
IntAndStr = Inherit Int and Str # SyntaxError: multiple inheritance of classes is not allowed
```
However, multiple inherited Python classes can be used.
## Multi-layer (multi-level) Inheritance
Erg inheritance also prohibits multi-layer inheritance. That is, you cannot define a class that inherits from another class.
Inheritable classes that inherit from an `Object` may exceptionally inherit.
Also in this case, Python's multi-layered inherited classes can be used.
## Rewriting Inherited Attributes
Erg does not allow rewriting the attributes inherited from the base class. This has two implications.
The first is an update operation on the inherited source class attribute. It cannot be reassigned, nor can it be updated by the `.update!` method, for example.
Overriding is different from rewriting because it is an operation to override with a more specialized method. Overrides must also be replaced by compatible types.
```erg
@Inheritable
Base! = Class {.pub = !Int; pri = !Int}
Base!
var = !1
inc_pub! ref! self = self.pub.update! p -> p + 1
Inherited! = Inherit Base!
Inherited!
var.update! v -> v + 1
# TypeError: can't update base class variables
@Override
inc_pub! ref! self = self.pub + 1
# OverrideError: `.inc_pub!` must be subtype of `Self! () => ()`
```
The second is an update operation on the (variable) instance attribute of the inherited source. This is also prohibited. Instance attributes of the base class may only be updated from methods provided by the base class.
Regardless of the visibility of the attribute, it cannot be updated directly. However, they can be read.
```erg
@Inheritable
Base! = Class {.pub = !Int; pri = !Int}
Base!
inc_pub! ref! self = self.pub.update! p -> p + 1
inc_pri! ref! self = self::pri.update! p -> p + 1
self = self.pub.update!
Inherited!
# OK
add2_pub! ref! self =
self.inc_pub!()
self.inc_pub!()
# NG, `Child` cannot touch `self.pub` and `self::pri`.
add2_pub! ref! self =
self.pub.update! p -> p + 2
```
After all, Erg inheritance can only add new attributes and override base class methods.
## Usage of Inheritance
While inheritance is a powerful feature when used correctly, it also has the drawback that it tends to complicate class dependencies, especially when multiple or multi-layer inheritance is used. Complicated dependencies can reduce code maintainability.
The reason Erg prohibits multiple and multi-layer inheritance is to reduce this risk, and the class patch feature was introduced to reduce the complexity of dependencies while retaining the "add functionality" aspect of inheritance.
So, conversely, where should inheritance be used? One indicator is when "semantic subtypes of the base class are desired.
Erg allows the type system to automatically do part of the subtype determination (e.g., Nat, where Int is greater than or equal to 0).
However, for example, it is difficult to create a "string type representing a valid e-mail address" relying solely on Erg's type system. You should probably perform validation on a normal string. Then, we would like to add some kind of "warrant" to the string object that has passed validation. That is the equivalent of downcasting to an inherited class. Downcasting a `Str object` to `ValidMailAddressStr` is a one-to-one correspondence with validating that the string is in the correct email address format.
```erg
ValidMailAddressStr = Inherit Str
ValidMailAddressStr.
init s: Str =
validate s # mail-address validation
Self.new s
s1 = "invalid mail address"
s2 = "foo@gmail.com"
_ = ValidMailAddressStr.init s1 # panic: invalid mail address
valid = ValidMailAddressStr.init s2
valid: ValidMailAddressStr # assurance that it is in the correct email address format
```
Another indicator is when you want to achieve a nominal polymorphism.
For example, the `greet!` procedure defined below will accept any object of type `Named`.
But obviously it is wrong to apply a `Dog` type object. So we will use the `Person` class for the argument type.
This way, only `Person` objects, classes that inherit from them, and `Student` objects will be accepted as arguments.
This is more conservative and avoids unnecessarily assuming too much responsibility.
```erg
Named = {name = Str; ...}
Dog = Class {name = Str; breed = Str}
Person = Class {name = Str}
Student = Inherit Person, additional: {id = Int}
structural_greet! person: Named =
print! "Hello, my name is {person::name}."
greet! person: Person =
print! "Hello, my name is {person::name}."
max = Dog.new {name = "Max", breed = "Labrador"}
john = Person.new {name = "John"}
alice = Student.new {name = "Alice", id = 123}
structural_greet! max # Hello, my name is Max.
structural_greet! john # Hello, my name is John.
greet! alice # Hello, my name is Alice.
greet! max # TypeError:
```
<p align='center'>
<a href='. /04_class.md'>Previous</a> | <a href='. /06_nst_vs_sst.md'>Next</a>
</p>

View file

View file

@ -0,0 +1,222 @@
# Patch
Erg does not allow modification of existing types and classes.
This means, it is not possible to define additional methods in a class, nor to perform specialization (a language feature that monomorphizes a polymorphically declared type and defines a dedicated method, as in C++).
However, there are many situations where you may want to add feature to an existing type or class, and there is a function called "patching" that allows you to do this.
```erg
StrReverse = Patch Str
StrReverse.
reverse self = self.iter().rev().collect(Str)
assert "abc".reverse() == "cba"
```
The name of the patch should be a straightforward description of the primary functionality to be added.
This way, objects of the type being patched (`Str`) can use the methods of the patch (`StrReverse`).
In fact, built-in method `.reverse` is not a method of `Str`, but a method added to `StrRReverse`.
However, patch methods have lower precedence than methods of the nominal type (class/trait) and cannot override methods of existing types.
```erg
StrangeInt = Patch Int
StrangeInt.
`_+_` = Int.`_-_` # AssignError: . `_+_` is already defined in Int
```
If you want to override, you must inherit from the class.
However, it is basically recommended not to override and to define a method with a different name.
Overriding is not very easy to do because of some safety restrictions.
```erg
StrangeInt = Inherit Int
StrangeInt.
# Overriding methods must be given Override decorators.
# In addition, you need to override all Int methods that depend on Int.`_+_`.
@Override
`_+_` = Super.`_-_` # OverrideError: Int.`_+_` is referenced by ... ````` , so these methods must also be overridden
```
## Selecting Patches
Patches can be defined for a single type, and can be grouped together.
```erg
# foo.er
StrReverse = Patch(Str)
StrReverse.
reverse self = ...
StrMultiReplace = Patch(Str)
StrMultiReverse.
multi_replace self, pattern_and_targets: [(Pattern, Str)] = ...
StrToCamelCase = Patch(Str)
StrToCamelCase.
to_camel_case self = ...
StrToKebabCase = Patch(Str)
StrToKebabCase.
to_kebab_case self = ...
StrBoosterPack = StrReverse and StrMultiReplace and StrToCamelCase and StrToKebabCase
StrBoosterPack = StrReverse and StrMultiReplace and StrToCamelCase and StrToKebabCase
```
```erg
{StrBoosterPack; ...} = import "foo"
assert "abc".reverse() == "cba"
assert "abc".multi_replace([("a", "A"), ("b", "B")]) == "ABc"
assert "to camel case".to_camel_case() == "toCamelCase"
assert "to kebab case".to_kebab_case() == "to-kebab-case"
```
If multiple patches are defined, some of them may result in duplicate implementations.
```erg
# foo.er
StrReverse = Patch(Str)
StrReverse.
reverse self = ...
# more efficient implementation
StrReverseMk2 = Patch(Str)
StrReverseMk2.
reverse self = ...
"hello".reverse() # PatchSelectionError: multiple choices of `.reverse`: StrReverse, StrReverseMk2
```
In such a case, you can make it unique by using the __related function__ form instead of the method form.
```erg
assert StrReverseMk2.reverse("hello") == "olleh"
```
You can also make it unique by selectively importing.
```erg
{StrReverseMk2; ...} = import "foo"
assert "hello".reverse() == "olleh"
```
## Glue Patch
Patches can also relate types to each other. The `StrReverse` patch relates `Str` and `Reverse`.
Such a patch is called a __glue patch__.
Because `Str` is a built-in type, a glue patch is necessary for users to retrofit traits.
```erg
Reverse = Trait {
.reverse = Self.() -> Self
}
StrReverse = Patch Str, Impl: Reverse
StrReverse.
reverse self =
self.iter().rev().collect(Str)
```
Only one glue patch can be defined per type/trait pair.
This is because if multiple glue patches were "visible" at the same time, it would not be possible to uniquely determine which implementation to choose.
However, you can swap patches when moving to another scope (module).
```erg
NumericStr = Inherit Str
NumericStr.
...
NumStrRev = Patch NumericStr, Impl: Reverse
NumStrRev.
...
# DuplicatePatchError: NumericStr is already associated with `Reverse`
# hint: `Str` (superclass of `NumericStr`) is associated with `Reverse` by `StrReverse`
```
## Appendix: Relationship to Rust's Trait
Erg patches are the equivalent of Rust's (retrofitted) `impl` blocks.
```rust
// Rust
trait Reverse {
fn reverse(self) -> Self;
}
impl Reverse for String {
fn reverse(self) -> Self {
self.chars().rev().collect()
}
}
```
You could say that Rust's traits are features of Erg's traits and patches. This makes Rust's traits sound more convenient, but that is not necessarily the case.
```erg
# Erg
Reverse = Trait {
.reverse = Self.() -> Self
}
StrReverse = Patch(Str, Impl: Reverse)
StrReverse.
reverse self =
self.iter().rev().collect(Str)
```
Because the `impl` block is objectized as a patch in Erg, selective inclusion is possible when importing from other modules. As a side-effect, it also allows implementation of external traits to external structures.
Also, syntaxes such as `dyn trait` and `impl trait` are no longer required by the structure type.
```erg
# Erg
reversible: [Reverse; 2] = [[1, 2, 3], "hello"]
iter|T|(i: Iterable T): Iterator T = i.iter()
```
```rust
// Rust
let reversible: [Box<dyn Reverse>; 2] = [Box::new([1, 2, 3]), Box::new("hello")];
fn iter<I>(i: I) -> impl Iterator<Item = I::Item> where I: IntoIterator {
i.into_iter()
}
```
## For-All Patch
A patch can be defined not only for one specific type, but also for "function types in general" and so on.
In this case, the term to which the degree of freedom is to be given is given as an argument (in the case below, `T: Type`). A patch defined in this way is called an all-symmetric patch.
As you can see, an all-symmetric patch is precisely a function that returns a patch, but it can also be considered a patch in its own right.
```erg
FnType T: Type = Patch(T -> T)
FnType(T).
type = T
assert (Int -> Int).type == Int
```
## Structural Patch
In addition, patches can be defined for any type that satisfies a certain structure.
However, this has a lower priority than nominal patches and class methods.
Careful design should be used when defining structural patches, as some properties are lost by extension, such as the following.
```erg
# This should not be `Structural`
Norm = Structural Patch {x = Int; y = Int}
Norm.
norm self = self::x**2 + self::y**2
Point2D = Class {x = Int; y = Int}
assert Point2D.new({x = 1; y = 2}).norm() == 5
Point3D = Class {x = Int; y = Int; z = Int}
assert Point3D.new({x = 1; y = 2; z = 3}).norm() == 14 # AssertionError:
```
<p align='center'>
<a href='. /06_nst_vs_sst.md'>Previous</a> | <a href='. /08_value.md'>Next</a>
</p>

View file

@ -0,0 +1,37 @@
# Value Type
Value types are Erg built-in types that can be evaluated at compile time, specifically:
``` erg
Value = (
Int
or Nat
or Ratio
or Float
or Complex
or Bool
or Str
or NoneType
or Array Const
or Tuple Const
or Set Const
or ConstFunc(Const, _)
or ConstProc(Const, _)
or ConstMethod(Const, _)
)
```
Value-type objects, constants, and compile-time subroutines applied to them are called __constant expressions__.
``` erg
1, 1.0, 1+2im, True, None, "aaa", [1, 2, 3], Fib(12)
```
Be careful with subroutines. Subroutines may or may not be value types.
Since the substance of a subroutine is just a pointer, it can be treated as a value [<sup id="f1">1</sup>](#1), but when compiling something that is not a subroutine cannot be used in a constant context. is not a value type because it doesn't make much sense.
Types classified as value types may be added in the future.
---
<span id="1" style="font-size:x-small"><sup>1</sup> The term "value type" in Erg differs from the definition in other languages. There is no concept of memory within pure Erg semantics, and it is incorrect to state that it is a value type because it is placed on the stack, or that it is not a value type because it is actually a pointer. A value type only means that it is a `Value` type or its subtypes. [](#f1)</span>

View file

View file

View file

View file

@ -0,0 +1,75 @@
# Refinement Type
Refinement type is a type constrained by a predicate expression. Enumeration types and interval types are syntax sugar of refinement types.
The standard form of a refinement type is `{Elem: Type | (Pred)*}`. This means that the type is a type whose elements are `Elem` satisfying `Pred`.
The type that can be used for the sifting type is [Const type](./advanced/const.md) only.
```erg
Nat = 0.. _
Odd = {N: Int | N % 2 == 1}
Char = StrWithLen 1
# StrWithLen 1 == {_: StrWithLen N | N == 1}
[Int; 3] == {_: Array Int, N | N == 3}
Array3OrMore == {A: Array _, N | N >= 3}
```
When there are multiple preds, they can be separated by `;` or `and` or `or`. `;` and `and` mean the same thing.
The elements of `Odd` are `1, 3, 5, 7, 9, ...`.
It is called a refinement type because it is a type whose elements are part of an existing type as if it were a refinement.
The `Pred` is called a (left-hand side) predicate expression. Like assignment expressions, it does not return a meaningful value, and only a pattern can be placed on the left-hand side.
That is, expressions such as `X**2 - 5X + 6 == 0` cannot be used as refinement-type predicate expressions. In this respect, it differs from a right-hand-side predicate expression.
```erg
{X: Int | X**2 - 5X + 6 == 0} # SyntaxError: the predicate form is invalid. Only names can be on the left-hand side
```
If you know how to solve quadratic equations, you would expect the above refinement form to be equivalent to `{2, 3}`.
However, the Erg compiler has very little knowledge of algebra, so it cannot solve the predicate on the right.
## Smart Cast
It's nice that you defined `Odd`, but as it is, it doesn't look like it can be used much outside of literals. To promote an odd number in a normal `Int` object to `Odd`, i.e., to downcast an `Int` to `Odd`, you need to pass the constructor of `Odd`.
For refinement types, the normal constructor `.new` may panic, and there is an auxiliary constructor called `.try_new` that returns a `Result` type.
```erg
i = Odd.new (0..10).sample!()
i: Odd # or Panic
```
It can also be used as a type specification in `match`.
```erg
# i: 0..10
i = (0..10).sample!
match i:
o: Odd ->
log "i: Odd"
n: Nat -> # 0..10 < Nat
log "i: Nat"
```
However, Erg cannot currently make sub-decisions such as `Even` because it was not `Odd`, etc.
## Enumerated, Interval and Sift Types
The enumerative/interval types introduced before are syntax sugar of the refinement type.
`{a, b, ...}` is `{I: Typeof(a) | I == a or I == b or ... }`, and `a..b` is desugarized to `{I: Typeof(a) | I >= a and I <= b}`.
```erg
{1, 2} == {I: Int | I == 1 or I == 2}
1..10 == {I: Int | I >= 1 and I <= 10}
1... <10 == {I: Int | I >= 1 and I < 10}
```
## Refinement pattern
Just as `_: {X}` can be rewritten as `X` (constant pattern), `_: {X: T | Pred}` can be rewritten as `X: T | Pred`.
```erg
# method `.m` is defined for arrays of length 3 or greater
Array(T, N | N >= 3)
.m(&self) = ...
```

View file

View file

@ -0,0 +1,74 @@
# Dependent Types
Dependent types are one of the most important features of Erg.
Dependent types are types that take values as arguments. Normal polymorphic types can take only types as arguments, but dependent types loosen that restriction.
Dependent types, such as `[T; N]`(`Array(T, N)`), are equivalent.
This type depends not only on the type `T` of the contents, but also on the number `N` of the contents. `N` contains objects of type `Nat`.
```erg
a1 = [1, 2, 3].
assert a1 in [Nat; 3].
a2 = [4, 5, 6, 7]
assert a1 in [Nat; 4].
assert a1 + a2 in [Nat; 7].
```
If the type object passed as a function argument is related to a return type, write the following
```erg
narray: |N: Nat| {N} -> [{N}; N]
narray(N: Nat): [N; N] = [N; N]
assert narray(3) == [3, 3, 3].
```
When defining a dependent type, all type arguments must be constants.
Dependent types already exist in some languages, but Erg has the unique feature of allowing you to define procedural methods on dependent types.
```erg
x = 1
f x =
print! f::x, module::x
# Phantom types have an attribute called Phantom that has the same value as the type argument
T X: Int = Class Impl: Phantom X
T(X).
x self = self::Phantom
T(1).x() # 1
```
Type arguments of variable-dependent types can be transitioned by applying methods.
Transitions are specified with `~>`.
```erg
# Note that `Id` is an immutable type and cannot be transitioned.
VM!(State: {"stopped", "running"}! |= _, Id: Nat |= _) = Class(... State).
VM!().
# Variables that do not change can be omitted by passing `_`.
start! ref! self("stopped" ~> "running") =
self.initialize_something!
self::set_phantom!("running")
# You can also cut out each type argument (only within the defined module)
VM!.new() = VM!(!" stopped", 1).new()
VM!("running" ~> "running").stop! ref! self =
self.close_something!()
self::set_phantom!("stopped"))
vm = VM!.new()
vm.start!()
vm.stop!()
vm.stop!() # TypeError: VM! stopped", 1) doesn't have .stop!
# TypeError: VM! running", 1) has .stop!
```
You can also create dependent types by incorporating or inheriting from existing types.
```erg
MyArray(T, N) = Inherit [T; N].
# type of self: Self(T, N) in conjunction with .array!
MyStruct!(T, N: Nat!) = Class {.array: [T; !N]}
```

View file

@ -0,0 +1,280 @@
# Type Variable, quantified type
A type variable is a variable used, for example, to specify the type of subroutine arguments, and its type is arbitrary (not monomorphic).
First, as motivation for introducing type variables, consider the `id` function, which returns input as is.
```erg
id x: Int = x
```
The `id` function that returns the input as is is defined for the type `Int`, but this function can obviously be defined for any type.
Let's use `Object` for the largest class.
```erg
id x: Object = x
i = id 1
s = id "foo"
b = id True
```
Sure, it now accepts arbitrary types, but there is one problem: the return type is expanded to `Object`. The return type is expanded to `Object`.
I would like to see the return type `Int` if the input is of type `Int`, and `Str` if it is of type `Str`.
```erg
print! id 1 # <Object object>
id(1) + 1 # TypeError: cannot add `Object` and `Int
```
To ensure that the type of the input is the same as the type of the return value, use a __type variable__.
Type variables are declared in `||`(type variable list).
```erg
id|T: Type| x: T = x
assert id(1) == 1
assert id("foo") == "foo"
assert id(True) == True
```
This is called the __universal quantification (universalization)__ of the function. There are minor differences, but it corresponds to the function called generics in other languages. A universalized function is called a __polymorphic function__.
Defining a polymorphic function is like defining a function of the same form for all types (Erg prohibits overloading, so the code below cannot really be written).
```erg
id|T: Type| x: T = x
# pseudo code
id x: Int = x
id x: Str = x
id x: Bool = x
id x: Ratio = x
id x: NoneType = x
...
```
Also, the type variable `T` can be inferred to be of type `Type` since it is used in the type specification. So `|T: Type|` can simply be abbreviated to `|T|`.
You can also omit `|T, N| foo: [T; N]` if it can be inferred to be other than a type object (`T: Type, N: Nat`).
You can also provide constraints if the type is too large for an arbitrary type.
Constraints also have advantages, for example, a subtype specification allows certain methods to be used.
```erg
# T <: Add
# => T is a subclass of Add
# => can do addition
add|T <: Add| l: T, r: T = l + r
```
In this example, `T` is required to be a subclass of type `Add`, and the actual types of `l` and `r` to be assigned must be the same.
In this case, `T` is satisfied by `Int`, `Ratio`, etc. So, the addition of `Int` and `Str`, for example, is not defined and is therefore rejected.
You can also type it like this.
```erg
f|
Y, Z: Type
X <: Add Y, O1
O1 <: Add Z, O2
O2 <: Add X, _
| x: X, y: Y, z: Z =
x + y + z + x
```
If the annotation list is long, you may want to pre-declare it.
```erg
f: |Y, Z: Type, X <: Add(Y, O1), O1 <: Add(Z, O2), O2 <: Add(X, O3)| (X, Y, Z) -> O3
f|X, Y, Z| x: X, y: Y, z: Z =
x + y + z + x
```
Unlike many languages with generics, all declared type variables must be used either in the temporary argument list (the `x: X, y: Y, z: Z` part) or in the arguments of other type variables.
This is a requirement from Erg's language design that all type variables are inferrable from real arguments.
So information that cannot be inferred, such as the return type, is passed from real arguments; Erg allows types to be passed from real arguments.
```erg
Iterator T = Trait {
# Passing return types from arguments.
# .collect: |K: Type -> Type| Self(T). ({K}) -> K(T)
.collect(self(T), K: Type -> Type): K(T) = ...
...
}
it = [1, 2, 3].iter().map i -> i + 1
it.collect(Array) # [2, 3, 4].
```
Type variables can only be declared during `||`. However, once declared, they can be used anywhere until they exit scope.
```erg
f|X|(x: X): () =
y: X = x.clone()
log X.__name__
log X
f 1
# Int
# <class Int>
```
You can also explicitly monophasize at the time of use as follows
```erg
f: Int -> Int = id|Int|
```
In that case, the specified type takes precedence over the type of the actual argument (failure to match will result in a type error that the type of the actual argument is wrong).
That is, if the actual object passed can be converted to the specified type, it will be converted; otherwise, a compile error will result.
```erg
assert id(1) == 1
assert id|Int|(1) in Int
assert id|Ratio|(1) in Ratio
# You can also use keyword arguments
assert id|T: Int|(1) == 1
id|Int|("str") # TypeError: id|Int| is type `Int -> Int` but got Str
```
When this syntax is batting against comprehensions, you need to enclose it in `()`.
```erg
# {id|Int| x | x <- 1..10} would be interpreted as {id | ...} will be interpreted as.
{(id|Int| x) | x <- 1..10}
```
A type variable cannot be declared with the same name as a type that already exists. This is because all type variables are constants.
```erg
I: Type
# ↓ invalid type variable, already exists
f|I: Type| ... = ...
```
## Type arguments in method definitions
Type arguments on the left-hand side are treated as bound variables by default.
```erg
K(T: Type, N: Nat) = ...
K(T, N).
foo(x) = ...
```
Using another type variable name will result in a warning.
```erg
K(T: Type, N: Nat) = ...
K(U, M). # Warning: K's type variable names are 'T' and 'N'
foo(x) = ...
```
Constants are the same in all namespaces since their definition, so of course they cannot be used for type variable names.
```erg
N = 1
K(N: Nat) = ... # NameError: N is already defined
L(M: Nat) = ...
# Defined only if M == N == 1
L(N).
foo(self, x) = ...
# defined for any M: Nat
L(M).
.bar(self, x) = ...
```
You cannot have multiple definitions for each type argument, but you can define methods with the same name because there is no relationship between dependent types that are not assigned type arguments (non-primitive-kind) and dependent types that are assigned (primitive-kind).
```erg
K(I: Int) = ...
K.
# K is not a true type (atomic Kind), so we cannot define a method
# This is not a method (more like a static method)
foo(x) = ...
K(0).
foo(self, x): Nat = ...
```
## All symmetric types
The `id` function defined in the previous section is a function that can be of any type. So what is the type of the `id` function itself?
```erg
print! classof(id) # |T: Type| T -> T
```
We get a type `|T: Type| T -> T`. This is called a __closed universal quantified type/universal type__, which is `['a. ...]'` in ML, and `forall t. ...` in Haskell. Why the adjective "closed" is used is discussed below.
There is a restriction on the closed universal quantified type: only subroutine types can be made universal quantified, i.e., only subroutine types can be placed in the left clause. But this is sufficient, since subroutines are the most basic control structure in Erg, so when we say "I want to handle arbitrary X," i.e., I want a subroutine that can handle arbitrary X. So, the quantified type has the same meaning as the polymorphic function type. From now on, this type is basically called polymorphic function type.
Like anonymous functions, polymorphic types have arbitrary type variable names, but they all have the same value.
```erg
assert (|T: Type| T -> T) == (|U: Type| U -> U)
```
The equality is satisfied when there is an alpha equivalence, as in the lambda calculus. Since there are some restrictions on operations on types, equivalence determination is always possible (if we don't consider the stoppage property).
## Subtyping of Polymorphic Function Types
A polymorphic function type can be any function type. This means that there is a subtype relationship with any function type. Let's look at this relationship in detail.
A type in which the type variable is defined on the left-hand side and used on the right-hand side, such as `OpenFn T: Type = T -> T`, is called an __open universal type__.
In contrast, a type in which type variables are defined and used on the right-hand side, such as `ClosedFn = |T: Type| T -> T`, is called a __closed universal type__.
An open universal type is a supertype of all isomorphic "true" types. In contrast, a closed universal type is a subtype of all isomorphic true types.
```erg
(|T: Type| T -> T) < (Int -> Int) < (T -> T)
```
You may remember that closed ones are smaller/open ones are larger.
But why is this so? For a better understanding, let's consider an instance of each.
```erg
# id: |T: Type| T -> T
id|T|(x: T): T = x
# iid: Int -> Int
iid(x: Int): Int = x
# return arbitrary function as is
id_arbitrary_fn|T|(f1: T -> T): (T -> T) = f
# id_arbitrary_fn(id) == id
# id_arbitrary_fn(iid) == iid
# return the poly correlation number as it is
id_poly_fn(f2: (|T| T -> T)): (|T| T -> T) = f
# id_poly_fn(id) == id
id_poly_fn(iid) # TypeError
# Return Int type function as is
id_int_fn(f3: Int -> Int): (Int -> Int) = f
# id_int_fn(id) == id|Int|
# id_int_fn(iid) == iid
```
Since `id`, which is of type `|T: Type| T -> T`, can be assigned to a parameter `f3` of type `Int -> Int`, we may consider `(|T| T -> T) < (Int -> Int)`.
Conversely, `iid`, which is of type `Int -> Int`, cannot be assigned to parameter `f2` of type `(|T| T -> T)`, but it can be assigned to parameter `f1` of type `T -> T`, so `(Int -> Int) < (T -> T)`.
Therefore, it is indeed `(|T| T -> T) < (Int -> Int) < (T -> T)`.
## Quantified Types and Dependent Types
What is the relationship between dependent types and quantified types (polymorphic function types) and what is the difference between them?
We can say that a dependent type is a type that takes arguments, and an quantified type is a type that gives arbitrariness to the arguments.
The important point is that there are no type arguments in the closed, polymorphic type itself. For example, the polymorphic function type `|T| T -> T` is a type that takes a polymorphic function __only__, and its definition is closed. You cannot define methods, etc. using its type argument `T`.
In Erg, the type itself is also a value, so types that take arguments, such as function types, will probably be dependent types. In other words, polymorphic function types are both a quantified type and a dependent type.
```erg
PolyFn = Patch(|T| T -> T)
PolyFn.
type self = T # NameError: cannot find 'T'
DepFn T = Patch(T -> T)
DepFn.
type self =
log "by DepFn"
T
assert (Int -> Int).type() == Int # by DepFn
assert DepFn(Int).type() == Int # by DepFn
```

135
doc/EN/tips.md Normal file
View file

@ -0,0 +1,135 @@
# Tips
## Want to change the language in which errors are displayed
Please download Erg for your language.
However, external libraries may not support multiple languages.
## Want to change only certain attributes of a record
```erg
record: {.name = Str; .age = Nat; .height = CentiMeter}
{height; rest; ...} = record
mut_record = {.height = !height; ...rest}
```
## Want to shadow variables
Shadowing in the same scope is not possible with Erg. However, you can redefine them if the scope changes (This is a syntax called instance block).
````erg
## Get a T!-type object and finally assign it to a variable as type T
x: T =
x: T! = foo()
x.bar!()
x.freeze()
````
## Want to reuse a final class (non-inheritable class) somehow
You can create a wrapper class. This is a so-called composition pattern.
```erg
FinalWrapper = Class {inner = FinalClass}
FinalWrapper.
method self =
self::inner.method()
...
```
## Want to use an enumerated type that is not a string
You can define a traditional enumerated type (algebraic data type) commonly found in other languages as follows
If you implement `Singleton`, classes and instances are identical.
Also, if you use `Enum`, the type of choice is automatically defined as a redirect attribute.
```erg
Ok = Class Impl: Singleton
Err = Class Impl: Singleton
ErrWithInfo = Inherit {info = Str}
Status = Enum Ok, Err, ErrWithInfo
stat: Status = Status.cons(ErrWithInfo) {info = "error caused by ..."}
match! stat:
Status.Ok -> ...
Status.Err -> ...
Status.ErrWithInfo.{info;} -> ...
```
```erg
Status = Enum Ok, Err, ErrWithInfo
# is equivalent to
Status = Class Ok or Err or ErrWithInfo
Status.
Ok = Ok
Err = Err
ErrWithInfo = ErrWithInfo
```
## I want to enumerate at the beginning of 1
method 1:
```erg
arr = [...]
for! arr.iter().enumerate(start: 1), i =>
...
```
method 2:
```erg
arr = [...]
for! arr.iter().zip(1...) , i =>
...
```
## Want to test a (white box) non-public API
The private API in `foo.er` is specially accessible in the module `foo.test.er`.
The `foo.test.er` module cannot be imported, so it remains hidden.
```erg
# foo.er
private x = ...
```
```erg
# foo.test.er
foo = import "foo"
@Test
'testing private' x =
...
y = foo::private x
...
```
## Want to define a (variable) attribute that is read-only from the outside
You can make the attribute private and define a getter.
```erg
C = Class {v = Int!}
C::
inc_v!(ref! self) = self::v.inc!()
...
C.
get_v(ref self): Int = self::v.freeze()
...
```
## Want the argument names to be identified on the type system
You can receive arguments by record.
```erg
Point = {x = Int; y = Int}
norm: Point -> Int
norm({x: Int; y: Int}): Int = x**2 + y**2
assert norm({x = 1; y = 2}) == norm({y = 2; x = 1})
```
## Want to stop warnings
There is no option in Erg to stop warnings (this is by design). Please rewrite your code.

13
doc/JA/API/consts.md Normal file
View file

@ -0,0 +1,13 @@
# 組み込み定数
## True
## False
## None
## Ellipsis
## NotImplemented
## Inf

121
doc/JA/API/funcs.md Normal file
View file

@ -0,0 +1,121 @@
# 関数
## 基本関数
### if|T; U|(cond: Bool, then: T, else: U) -> T or U
### map|T; U|(i: Iterable T, f: T -> U) -> Map U
Pythonとは引数の順番が逆なので注意。
### log(x: Object, type: LogType = Info) -> None
`x`をデバッグ表示でログに残す。ログは、実行が終了した後にまとめて表示される。
絵文字対応ターミナルでは`type`に応じてプレフィックスがつく。
* type == Info: 💬
* type == Ok: ✅
* type == Warn: ⚠️
* type == Hint: 💡
### panic(msg: Str) -> Panic
msgを表示して停止する。
絵文字対応ターミナルでは🚨がプレフィックスに付く。
### discard|T|(x: ...T) -> NoneType
`x`を捨てる。戻り値を使用しないときなどに使う。`del`とは違い、変数`x`を参照できなくするわけではない。
```erg
p! x =
# q!は何らかのNoneや()でない値を返すとする
# 要らない場合は`discard`を使う
discard q!(x)
f x
discard True
assert True # OK
```
### import(path: Path) -> Module or CompilerPanic
モジュールをインポートする。モジュールが見つからない場合、コンパイルエラーを送出する。
### eval(code: Str) -> Object
codeをコードとして評価し返す。
### classof(object: Object) -> Class
`object`のクラスを返す。
ただしクラスは比較できないため、インスタンス判定がしたい場合は`classof(object) == Class`ではなく`object in Class`を使う。
コンパイル時に決定される構造型は`Typeof`で得られる。
## Iterator, Array生成系
### repeat|T|(x: T) -> RepeatIterator T
```erg
rep = repeat 1 # Repeater(1)
for! rep, i =>
print! i
# 1 1 1 1 1 ...
```
### dup|T; N|(x: T, N: Nat) -> [T; N]
```erg
[a, b, c] = dup new(), 3
print! a # <Object object>
print! a == b # False
```
### cycle|T|(it: Iterable T) -> CycleIterator T
```erg
cycle([0, 1]).take 4 # [0, 1, 0, 1]
cycle("hello").take 3 # "hellohellohello"
```
## 定数式関数
### Class
クラスを新しく生成する。`Inherit`とは違い、`Class`を通すとベース型からは独立し、メソッドは失われる。
比較もできなくなるが、パターンマッチなどは行える。
```erg
C = Class {i = Int}
NewInt = Class Int
Months = Class 1..12
jan = Months.new(1)
jan + Months.new(2) # TypeError: `+` is not implemented for 'Months'
match jan:
1 -> log "January"
_ -> log "Other"
```
第二引数のImplは実装するトレイトである。
### Inherit
クラスを継承する。基底クラスのメソッドをそのまま使用できる。
### Trait
トレイトを新しく生成する。現在のところ、指定できるのはレコード型のみ。
### Typeof
引数の型を返す。実行時のクラスを得たい場合は`classof`を使う。
型指定に使うとWarningが出る。
```erg
x: Typeof i = ...
# TypeWarning: Typeof(i) == Int, please replace it
```
### Deprecated
デコレータとして使用する。型や関数が非推奨であると警告する。

0
doc/JA/API/index.md Normal file
View file

57
doc/JA/API/modules/external/alstruct.md vendored Normal file
View file

@ -0,0 +1,57 @@
# alstruct
代数的構造を表すトレイトや、それにかかるパッチを提供するモジュール。
* member
## BinOp
```erg
BinOp Op: Kind 2 = Subsume Op(Self, Self.ReturnTypeOf Op), Additional: {
.ReturnTypeof = TraitType -> Type
}
Nat <: BinOp Add
assert Nat.ReturnTypeof(Add) == Nat
assert Nat.ReturnTypeof(Sub) == Int
assert Nat.ReturnTypeof(Mul) == Nat
assert Nat.ReturnTypeof(Div) == Positive Ratio
```
## SemiGroup
```erg
SemiGroup Op: Kind 2 = Op(Self, Self)
IntIsSemiGroupAdd = Patch Int, Impl=SemiGroup Add
Int <: SemiGroup Add
```
## Functor
```erg
## * Identity law: x.map(id) == x
## * Composition law: x.map(f).map(g) == x.map(f.then g)
Functor = Trait {
.map|T, U: Type| = (Self(T), T -> U) -> Self U
}
```
## Applicative
```erg
## * Identity law: x.app(X.pure(id)) == x
Applicative = Subsume Functor, Additional: {
.pure|T: Type| = T -> Self T
.app|T, U: Type| = (Self(T), Self(T -> U)) -> Self U
}
```
## Monad
```erg
Monad = Subsume Applicative, Additional: {
.bind|T, U: Type| = (Self(T), T -> Self U) -> Self U
}
```

View file

@ -0,0 +1,24 @@
# module `repl`
provides REPL(Read-Eval-Print-Loop)-related APIs.
## functions
* `gui_help`
オブジェクトに関する情報をブラウザで表示する。オフラインでも使用可能。
## types
### Guess = Object
#### methods
* `.guess`
与えられた引数と戻り値から、関数を推測する。
```erg
1.guess((1,), 2) # <Int.__add__ method>
[1, 2].guess((3, 4), [1, 2, 3, 4]) # <Array(T, N).concat method>
```

View file

@ -0,0 +1,6 @@
# module `status`
状態を表す型が定義されています。状況に応じて選択肢を外したりして使用してください。
* ExecResult = {"success", "warning", "failure", "fatal", "unknown"}
* ExecStatus = {"ready", "running", "sleeping", "plague", "completed", "terminated"}

View file

@ -0,0 +1,73 @@
# module `unit`
`unit`モジュールは数値計算でよく使われる単位を型として定義したモジュールです。
Ergの数値型は`Nat`, `Int`, `Ratio`などがあります。しかしこれらの型は「何を意味する数値なのか」という情報を持っておらず、メートルとヤード同士の足し算などといったナンセンスな計算を行えてしまいます。
`unit`モジュールを使うことにより、単位の違う数値を関数に渡すといったミスを防げます。
このようなミスは実際に起っており、[単位系の取り間違いで火星探査機が行方不明](http://www.sydrose.com/case100/287/)になるなど、深刻なバグを引き起こしかねません。
数値計算を行う上でコードの堅牢性を高めたいならばこのモジュールを使用しておくべきです。
```erg
{*} = import "unit"
x = 6m # `x = Meter.new(6)`と等価
t = 3s # `t = Sec.new(3)`と等価
# m/sは速度の単位オブジェクトで、Velocity型
print! x/t # 2m/s
print! x + 4m # 10m
print! x + 2s # TypeError: `+`(Meter, Sec) is not implemented
```
`m`, `s`, `m/s`というオブジェクトは単位オブジェクトと呼ばれます。それ自体で1m, 1s, 1m/sという意味を持ちます。`m/s`はmとsの合成によって生まれた単位オブジェクトといえます。
unitでは以下の単位を型として定義しています。SI(国際単位系)と呼ばれるものです。
* 長さMeter(単位定数: m)
* 質量KiloGram(単位定数: kg, g = 0.001kg)
* 時間Sec (分、時間、日、年などはSecから生成されたminute, hour, day, yearなどの定数がある)
* 電流: Amper(単位定数: a)
* 温度: Kelvin(単位定数: k, Fahren, Celsius型もあり、相互変換可能)
* 物質量: Mol(単位定数: mol)
* 光度: Candela(単位定数: cd)
また、`Unit1`, `UnitMul`, `UnitDiv`という型が定義されており、これを使用して基本型を合成し新しい単位を作成する事が可能です。
例えば、振動数の単位ヘルツ(hertz)は振動周期(秒)の逆数で定義されているので、`UnitDiv(Unit1, Sec)`です。
この型を意味のある型とみなしたい(専用のメソッドを加えたい、など)ときは、[パッチ](./../../syntax/type/07_patch.md)を作成すると良いでしょう。
```erg
Hertz = Patch UnitDiv(Unit1, Sec)
SquareMeter = Patch UnitMul(Meter, Meter)
```
補助単位も予めいくつか定義されています。
* 振動数: Hertz(hz)
* 力: Newton(newton)
* エネルギー: Joule(j)
* 仕事率: Watt(w)
* 電位: Volt(v)
* 電気抵抗: Ohm(ohm)
* 速度: Velocity(m/s)
* 面積: SquareMeter(m**2)
* 体積: CubicMeter(m**3) (litre = 10e-3 m**3)
* 角度: Degree(deg) (rad = 180/pi deg)
* 長さ: Feet, Yard, Inch, Mile, Ly, Au, Angstrom
* 重さ: Pond
また、接頭辞も定義しています。
* Femto = 1e-15
* Pico = 1e-12
* Nano = 1e-9
* Micro = 1e-6
* Milli = 1e-3
* Centi = 1e-2
* Deci = 1e-1
* Hecto = 1e+2
* Kilo = 1e+3
* Mega = 1e+6
* Giga = 1e+9
* Tera = 1e+12
* Peta = 1e+15
* Exa = 1e+18
※名前の由来に反して、Ergでは基本的にMKS単位系を採用しています。CGS単位系のunitモジュールが欲しい場合は、外部ライブラリ([cgs](https://github.com/mtshiba/cgs)等)を使用してください。

View file

@ -0,0 +1,24 @@
# module `unsound`
Provides APIs perform unsound and unsafe operations that cannot be guaranteed safe in Erg's type system.
## `unsafe!`
Executes a `Unsafe` procedure. Just like Rust, `Unsafe` APIs cannot be called directly, but are all passed as higher-order functions to this procedure.
```erg
unsound = import "unsound"
i = unsound.unsafe! do!:
# convert `Result Int` to `Int`
unsound.transmute input!().try_into(Int), Int
```
## transmute
第1引数のオブジェクトを第2引数の型へ変換します。型チェックは行われません。
この関数は型システムの型安全性を損ないます。使用の際はバリデーション等を行ってください。
## auto_transmute
`transmute`とは違い、期待される型に自動で変換します。Ocamlの`Obj.magic`と同じ働きをします。

64
doc/JA/API/operators.md Normal file
View file

@ -0,0 +1,64 @@
# 演算子
## 中置演算子
### `_+_`|R; O; A <: Add(R, O)|(x: A, y: R) -> O
加算を実行する。
### `_-_`|R; O; S <: Sub(R, O)|(x: S, y: R) -> O
減算を実行する。
### `*`|R; O; M <: Mul R, O|(x: M, y: R) -> O
乗算を実行する。
### `/`|R; O; D <: Div(R, O)|(x: D, y: R) -> O
除算を実行する。
## 中置アルファベット演算子
### `and`(x: Bool, y: Bool) -> Bool
and演算を実行する。
### `or`(x: Bool, y: Bool) -> Bool
and演算を実行する。
## 前置演算子
### `+_`|T <: Num|(x: T) -> T
デフォルトではidと同じ。
### `-_`|T <: Num|(x: T) -> T.Neg
例えば、Nat.`-`: Nat -> Negとなり、戻り値が違う。
### `!`|T <: Immut|(x: T) -> `T!`
不変オブジェクトから可変オブジェクトを生成する。
この演算子自体はProceduralではなく、関数内でも使える。
### `..`|T <: Ord|(x: T) -> Range T
x終わりで下界のないRangeオブジェクトを生成する。
x..xはイテレータとしてxのみ返す。
### `..<`|T <: Ord|(x: T) -> Range T
x..<xは空Rangeオブジェクトになりイテレータとして何も生成しない
## 後置演算子
後置演算子は構文解析上中置演算子の解析が失敗した際に呼び出される。
すなわち、`x..`が関数を返したとしても`x.. y``(..)(x, y)`であり`(x..)(y)`とは解釈されない。
### |T <: Ord|(x: T)`..` -> Range T
x始まりで上界のないRangeオブジェクトを生成する。
### |T <: Ord|(x: T)`<..` -> Range T

39
doc/JA/API/procs.md Normal file
View file

@ -0,0 +1,39 @@
# プロシージャ
## print!
```erg
print!(x) -> NoneType
```
xを改行ありで返す。
## debug&excl;
```erg
debug!(x, type = Info) -> NoneType
```
xを改行ありでデバッグ表示(ファイル名、行数、変数の場合変数名が一緒に表示される)する。リリースモードでは除去される。
絵文字対応ターミナルではtypeに応じてプレフィックスが付く。
* type == Info: 💬
* type == Ok: ✅
* type == Warn: ⚠️
* type == Hint: 💡
## for! i: Iterable T, block: T => NoneType
blockの動作でイテレータを走査する。
## while! cond: Bool!, block: () => NoneType
condがTrueの間、blockを実行する。
## Lineno!() -> Nat
## Filename!() -> Str
## Namespace!() -> Str
## Module!() -> Module

176
doc/JA/API/special.md Normal file
View file

@ -0,0 +1,176 @@
# 特殊形式(Special form)
特殊形式は、Ergの型システムでは表現ができない演算子、サブルーチン(のようなもの)である。``で囲っているが、実際は捕捉できない。
また、`Pattern``Body`, `Conv`といった型が便宜上登場するが、そのような型が存在するわけではない。その意味もコンテクストによって異なる。
## `=`(pat: Pattern, body: Body) -> NoneType
bodyをpatに変数として代入する。同じスコープにすでに変数が存在する場合と、patにマッチしなかった場合にエラーを送出する。
また、レコードの属性定義やデフォルト引数にも使われる。
```erg
record = {i = 1; j = 2}
f(x: Int, y = 2) = ...
```
bodyが型か関数であるときに`=`は特殊な振る舞いをする。
左辺の変数名を右辺のオブジェクトに埋め込むのである。
```erg
print! Class() # <class <lambda>>
print! x: Int -> x + 1 # <function <lambda>>
C = Class()
print! c # <class C>
f = x: Int -> x + 1
print! f # <function f>
g x: Int = x + 1
print! g # <function g>
K X: Int = Class(...)
print! K # <kind K>
L = X: Int -> Class(...)
print! L # <kind L>
```
`=`演算子は、戻り値が「未定義」である。
多重代入、関数中での`=`は文法エラーとなる。
```erg
i = j = 1 # SyntaxError: multiple assignments are not allowed
print!(x=1) # SyntaxError: cannot use `=` in function arguments
# hint: did you mean keyword arguments (`x: 1`)?
if True, do:
i = 0 # SyntaxError: A block cannot be terminated by an assignment expression
```
## `->`(pat: Pattern, body: Body) -> Func
無名関数、関数型を生成する。
## `=>`(pat: Pattern, body: Body) -> Proc
無名プロシージャ、プロシージャ型を生成する。
## `:`(subject, T)
subjectがTに合致しているか判定する。合致していない場合はコンパイルエラーを送出する。
```erg
a: Int
f x: Int, y: Int = x / y
```
また、`:`適用スタイルにも使われる。
```erg
f x:
y
z
```
`:``=`と同じく演算の結果が未定義である。
```erg
_ = x: Int # SyntaxError:
print!(x: Int) # SyntaxError:
```
## `.`(obj, attr)
objの属性を読み込む。
`x.[y, z]`とすると、xのyとzという属性を配列にして返す。
## `|>`(obj, c: Callable)
`c(obj)`を実行する。`x + y |>.foo()``(x + y).foo()`と同じ。
### (x: Option T)`?` -> T | T
後置演算子。`x.unwrap()`を呼び出し、エラーの場合はその場で`return`する。
## match(obj, ...lambdas: Lambda)
objについて、パターンにマッチしたlambdaを実行する。
```erg
match [1, 2, 3]:
(l: Int) -> log "this is type of Int"
[[a], b] -> log a, b
[...a] -> log a
# (1, 2, 3)
```
## del(x: ...T) -> NoneType | T
変数`x`を削除する。ただし組み込みのオブジェクトは削除できない。
```erg
a = 1
del a # OK
del True # SyntaxError: cannot delete a built-in object
```
## do(body: Body) -> Func
引数なしの無名関数を生成する。`() ->`の糖衣構文。
## do!(body: Body) -> Proc
引数なしの無名プロシージャを生成する。`() =>`の糖衣構文。
## `else`(l, r) -> Choice
Choiceオブジェクトというつ組のタプルのような構造体を生成する。
`l, r`は遅延評価される。すなわち、`.get_then`または`.get_else`が呼ばれたとき初めて式が評価される。
```erg
choice = 1 else 2
assert choice.get_then() == 1
assert choice.get_else() == 2
assert True.then(choice) == 1
```
## 集合演算子
### `[]`(...objs)
引数から配列、またはオプション引数からディクトを生成する。
### `{}`(...objs)
引数からセットを生成する。
### `{}`(...fields: ((Field, Value); N))
レコードを生成する。
### `{}`(layout, ...names, ...preds)
篩型、ランク2型を生成する。
### `...`
入れ子になったコレクションを展開する。パターンマッチでも使える。
```erg
[x, ...y] = [1, 2, 3]
assert x == 1 and y == [2, 3]
assert [x, ...y] == [1, 2, 3]
assert [...y, x] == [2, 3, 1]
{x; ...yz} = {x = 1; y = 2; z = 3}
assert x == 1 and yz == {y = 2; z = 3}
assert {x; ...yz} == {x = 1; y = 2; z = 3}
```
## 仮想演算子
ユーザーが直接使用できない演算子です。
### ref(x: T) -> Ref T | T
オブジェクトの不変参照を返す。
### ref!(x: T!) -> Ref! T! | T!
可変オブジェクトの可変参照を返す。

270
doc/JA/API/types.md Normal file
View file

@ -0,0 +1,270 @@
# Erg組み込み型一覧
型自体の属性は`.__dict__`の中には格納されていないので、インスタンスからは参照できない
## 汎用型 (Fundamental types)
### Object
* `__dir__`: オブジェクトの持つ属性を配列にして返す(dir関数)
* `__getattribute__`: 属性を取得して返す
* `__hash__`: オブジェクトのハッシュ値を返す
* `__repr__`: オブジェクトの文字列表現(リッチでない/デフォルト実装が存在)
* `__sizeof__`: オブジェクトのサイズ(ヒープに確保された分も含む)を返す
### Show
* `__str__`: オブジェクトの文字列表現(リッチな)を返す
### Fmt
* `__format__`: フォーマットされた文字列を返す
### Doc
* `__doc__`: オブジェクトの説明
### Named
* `__name__`: オブジェクトの名前
### Pickle
* `__reduce__`: Pickleによるオブジェクトのシリアライズを行う
* `__reduce_ex__`: プロトコルバージョンを指定できる__reduce__
## オブジェクトシステム
Trait classはPythonでのABC(抽象基底クラス、インターフェース)に相当
Instanceに属するのは1, True, "aaa"など
ClassはInt, Bool, Strなど
### Type
* `__supers__`: 上位型(`__mro__`は配列だが、こちらはSet)
* `__basicsize__`:
* `__dictoffset__`: Evmではサポートされない
* `__flags__`:
* `__itemsize__`: インスタンスのサイズ(Classでない場合は0)
* `__weakrefoffset__`: Evmではサポートされない
* `__membercheck__`: `ismember(x, T)`と同等
* `__subtypecheck__`: `issubtype(U, T)`と同等、`__subclasshook__`というエイリアスが存在(CPythonとの互換)
### Instance
* `__class__`: インスタンスの生成元クラスを返す(`.new`で生成されたオブジェクトは自動で付く)
### Class
* `__mro__`: メソッド解決用の型配列(自身も入っている、最後は必ずObject)
* `__base__`: ベースとなった型(複数ある場合、`__mro__[1]`)
* `__new__`: インスタンスを生成する
* `__init__`: インスタンスを初期化する
* `__init_subclass__`: インスタンスを初期化する
* `__intstancecheck__`: `MyClass.__instancecheck__(x)`などのように使う、`isinstance(x, MyClass)`と同等
* `__subclasscheck__`: `issubclass(C, MyClass)`と同等
## 演算子
ここで指定されている以外の演算子には特に専用の型はない
### Eq
* `__eq__(self, rhs: Self) -> Bool`: オブジェクトの比較関数(==)
* `__ne__`: オブジェクトの比較関数(!=)、デフォルト実装あり
### Ord
* `__lt__(self, rhs: Self) -> Bool`: オブジェクトの比較関数(<)
* `__le__`: オブジェクトの比較関数(<=)、デフォルト実装あり
* `__gt__`: オブジェクトの比較関数(>)、デフォルト実装あり
* `__ge__`: オブジェクトの比較関数(>=)、デフォルト実装あり
### BinAdd
* `__add__(self, rhs: Self) -> Self`: `+`を実装
### Add R, O
* `__add__(self, rhs: R) -> O`
### BinSub
* `__sub__(self, rhs: Self) -> Self`: `-`を実装
### Sub R, O
* `__sub__(self, rhs: R) -> O`
### BinMul
* `__mul__(self, rhs: Self) -> Self`: `*`を実装
* `__pow__`: `**`を実装(デフォルト実装あり)
### Mul R, O
* `__mul__(self, rhs: R) -> O`
* `__pow__`
### BinDiv
* `__div__(self, rhs: Self) -> Self`: `/`を実装、0によりパニックしてもよい
* `__mod__`: `%`を実装(デフォルト実装あり)
### Div R, O
* `__div__(self, rhs: R) -> O`
* `__mod__`
## 数値型
### Num (= Add and Sub and Mul and Eq)
Complex以外の例として、Vector, Matrix, TensorはNum(Matrix, Tensorの*はそれぞれdot, productと同じ)
### Complex (= Inherit(Object, Impl=Num))
* `imag: Ratio`: 虚部を返す
* `real: Ratio`: 実部を返す
* `conjugate self -> Complex`: 共役複素数を返す
### Float (= Inherit(FloatComplex, Impl=Num))
### Ratio (= Inherit(Complex, Impl=Num))
* `numerator: Int`: 分子を返す
* `denominator: Int`: 分母を返す
### Int (= Inherit Ratio)
### Nat (= Inherit Int)
* `times!`: self回procを実行する
## その他基本型
### Bool
* `__and__`:
* `__or__`:
* `not`:
## Str (<: Seq)
* `capitalize`
* `chomp`: 改行文字を除去
* `isalnum`:
* `isascii`:
* `isalpha`:
* `isdecimal`:
* `isdight`:
* `isidentifier`
* `islower`
* `isnumeric`
* `isprintable`
* `isspace`
* `istitle`
* `isupper`
* `lower`
* `swapcase`
* `title`
* `upper`
## その他
### Bit
* `from_bytes`: Bytesから変換
* `to_bytes`: Bytesへ変換(長さ(length)、エンディアン(byteorder)を指定)
* `bit_length`: Bit長を返す
### Iterable T
`Iterator`自体の型ではない点に注意。`Nat``Iterable`だが`Nat.next()`とはできず、`Nat.iter().next()`とする必要がある。
* `iter`: Iteratorを生成する。
### Iterator T
NatやRangeはIteratorを持っているので、`Nat.iter().map n -> n**2`, `(3..10).iter().fold (sum, n) -> sum + n*2`などが可能。
allやanyは使用後破壊されるので副作用なし。これらは副作用のない`next`を使って実装されていることになっているが、実行効率のため内部的には`Iterator!.next!`を使っている。
* `next`: 先頭の要素と残りのIteratorを返す。
* `all`
* `any`
* `filter`
* `filter_map`
* `find`
* `find_map`
* `flat_map`
* `flatten`
* `fold`
* `for_each`
* `map`
* `map_while`
* `nth`
* `pos`
* `take`
* `unzip`
* `zip`
### Iterator! T = Iterator T and ...
* `next!`: 先頭の要素を取り出す。
## SizedIterator T = Iterator T and ...
有限の要素を走査するIterator。
* `len`:
* `chain`:
* `count`:
* `is_empty`:
* `rev`:
* `next_back`:
* `nth_back`:
* `rfind`:
* `rfold`:
* `sum`:
* `max`:
* `min`:
## Seq T = SizedIterable T and ...
* `concat`: 2つのSeqを結合する
* `__getitem__`: `[]`によるアクセスと同等(なければパニック)
* `get`: __getitem__と違ってOptionで返す
* `maketrans`: 置換テーブルをつくる(スタティックメソッド)
* `replace`: 置換する
* `translate`: 置換テーブルにそって置換する
* `insert`: idx番目に追加
* `remove`: idx番目を取り出し
* `prepend`: 先頭に追加
* `dequeue`: 先頭を取り出し
* `push`: 最後尾に追加
* `pop`: 最後尾を取り出し
* `dedup`: 連続する値を削除
* `uniq`: 重複する要素を削除(sort |> dedupで実装されるため、順番が変わる可能性がある)
* `swap`: 要素を入れ替え
* `reverse`: 要素を反転
* `sort`: 要素をソート
* `first`:
* `last`:
### Seq! T (= Seq T and ...)
* `__setitem__!`:
* `__delitem__!`:
* `insert!`: idx番目に追加
* `remove!`: idx番目を取り出し
* `prepend!`: 先頭に追加
* `dequeue!`: 先頭を取り出し
* `push!`: 最後尾に追加
* `pop!`: 最後尾を取り出し
* `dedup!`: 連続する値を削除
* `uniq!`: 重複する要素を削除(sort! |> dedup!で実装されるため、順番が変わる可能性がある)
* `swap!`: 要素を入れ替え
* `reverse!`: 要素を反転
* `set!`
* `sort!`: 要素をソート
* `translate!`

View file

@ -0,0 +1,4 @@
# Array! T
可変長配列を表す型。コンパイル時に長さがわからない場合に使う。`[T]!`という糖衣構文がある。
`Array! T = ArrayWithMutLength! T, !_`で定義される。

View file

@ -0,0 +1,3 @@
# Array T: Type
`Array T = ArrayWithLen T, _`で定義される。`[T]`という糖衣構文がある。

View file

@ -0,0 +1,34 @@
# ArrayWithLen T: Type, N: Nat
`[T; N]`は糖衣構文。長さを省いた[`Array`型](./Array.md)もある。
## methods
* values_at(self, selectors: [Nat; N]) -> [T; N]
```erg
assert ["a", "b", "c", "d", "e"].values_at([0, 1, 3]) == ["a", "b", "d"]
```
* all(self, pred: T -> Bool) -> Bool
全ての要素がpredを満たすかどうかを返す。
要素が0のときはpredに関わらず`True`となるが、Warningを出す。
この仕様自体は多くの言語で採用されており、論理学的な整合性から要請される。
```erg
assert [].all(_ -> False)
```
```python
assert all(False for _ in [])
```
## methods of ArrayWithLen T, N | T <: Eq
* freq self -> [{T: Nat}]
オブジェクトの出現頻度を返す。
```erg
assert ["a", "b", "c", "b", "c", "b"].freq() \
== [{"a", 1}, {"b": 3}, {"c": 2}]
```

View file

@ -0,0 +1,26 @@
# ArrayWithMutLength! T: Type, N: Nat&excl;
コンパイル時に長さのわかる可変長配列。`ArrayWithMutLength(T, !N) == [T; !N]`という糖衣構文もある。
## methods
* push! ref! self(N ~> N+1, ...), elem: T
* pop! ref! (N ~> N-1, ...) -> T
* sample!(ref! self) -> T
* sample! ref! self, M: Nat -> [T; M]
中の要素をランダムに選んでコピーを返す。
* shuffle!(ref! self)
中身をシャッフルする。
* assert_len ref! self(_ ~> N, ...), N: Nat -> () or Panic
長さを検証する。
長さが不正な場合は`panic!`する。
## Impl
* From Range Int
* From [T; N]
* Num

View file

View file

@ -0,0 +1,14 @@
# Complex
複素数を表す型です。Ergで数字を表す型、例えばFloatやInt、Natなどは、大抵この型を上位に持ちます。
## supers
Num and Norm
## methods
* abs
* conjugate
* imag
* real

View file

@ -0,0 +1,7 @@
# Dict! K, V
辞書(ハッシュマップ)を表す型。`{K: V}`という構文糖が存在する。
## methods
* invert!(self) -> Self! V, K

View file

@ -0,0 +1,12 @@
# Either L, R = L or R
「LかRかどちらか」を表す型。Or型の2つ限定形と考えて良い。
## methods
* orl
* orr
* andl
* andr
* mapl
* mapr

View file

@ -0,0 +1,21 @@
# Float size
実数(小数を含む数)を表す型です。IEEE 754に準拠した浮動小数点数を表し、他の言語では一般的にfloatに相当する型です。
Float sizeのsizeは、8(1byte)~128(16byte)となります。単にFloatとした場合`Float 64`を表します。
Ergでの0.1は実はFloat型ではなく、Ratio型に属します。Float型のリテラルは存在せず、`(Ratioオブジェクト)f64`(e.g. (1/2)f64, 15f64)で生成します。f64は実数の1に対応します。
## supers
Complex and Ord
## methods
* sgn(self) -> {-1, 0, 1}
符号を返す。
* truncate(self) -> Int
自身に最も近い整数を返す。
* separate(self) -> [Str]
* separate(self, dight: Nat) -> [Str]
dight桁ごとに区切る。引数なしだと3。

View file

@ -0,0 +1,9 @@
# Function N: Nat
## methods of Function 1
* then(self, g: Self) -> Self
```erg
assert f(g(x)) == f.then(g) x
```

View file

@ -0,0 +1,7 @@
# Inf
Infはinfただひとつをインスタンスとするクラスである。
infの主な使いみちは、区間型での使用である。
例えば、2以上の整数型は`2..<inf`となり、0以下の実数は`-inf<..0.0`となる。
infは通常の意味での数ではないため四則演算をそのままでは定義できないが、
ExtNatなどいわゆる拡大数のクラスがライブラリで提供されている。

View file

@ -0,0 +1,10 @@
# Int
整数(Integer)を表すクラスです。インスタンスは0, 1, -1, 300000などがあります。
多くの言語では自然数を表す場合でもInt(に相当する型)を使用しますが、Ergではuse smaller typesの原則により、
Nat, NZInt, Interval型などを使用することが推奨されます。
IntはPythonに由来する型であり、その上位型には`Object`のみを持ちます。
## supers
## methods

View file

@ -0,0 +1,19 @@
# IntRange L, R
`L..R`のクラス。
```erg
IntRange L, R: Int == L..R
```
## methods
* .`_+_`: Self(L1, R1), Self(L2, R2) -> Self(L1+L2, R1+R2)
通常の加算。`Int``Nat`の加算はそれぞれのクラスで定義されていると見せかけて、ここで定義されている。
```erg
0..10 + 1..12 == 1..22
Int + 0..10 == _..|Int|_ + 0..10 == _..|Int|_ == Int
Nat + Nat == 0.._ + 0.._ == 0.._ == Nat
```

View file

@ -0,0 +1,18 @@
# Interval begin, end: WellOrder
整列集合型(WellOrder)の部分型を表す型です。Interval型にはPreOpen(x<..y)などの派生型が存在します
```erg
Months = 1..12
Alphabet = "a".."z"
Weekdays = Monday..Friday
Winter = November..December or January..February
```
```erg
0..1 # 整数の範囲
0.0..1.0 # 実数(有理数)の範囲
# or 0/1..1/1でも同じ
```
コンピュータは無限桁の数を上手く扱えないので、実数の範囲と言っても実際は有理数の範囲である。

View file

View file

@ -0,0 +1,5 @@
# Kind N: Nat
```erg
Kind N: Nat = (Type; N) -> Type
```

View file

@ -0,0 +1,7 @@
# Matrix T: Num, Shape: [M, N]
行列を表す型です。Tensor [M, N]を継承しています。
## def
Inherit Tensor T, [M, N]

View file

@ -0,0 +1,3 @@
# Module
## methods

View file

@ -0,0 +1,18 @@
# Nat
自然数を表す型。配列のインデックスや範囲型などで使われる。
## def
```erg
Nat = 0.._
```
## methods
* times!(self, p: () => NoneType) -> NoneType
```erg
100.times! () =>
print! "hello!"
```

View file

@ -0,0 +1,8 @@
# Neg
負の整数を表す型です。Pos and Neg and {0} == Intとなります。
ゼロ除算が起きない、Neg * Neg == Posとなるなど、いくつか特筆すべき性質も持っています。
## def
Inf<..-1

View file

@ -0,0 +1,13 @@
# Never
全ての型の下位型である。全てのメソッドを持っており、当然`.new`も持っているため`Class`である。しかしインスタンスは持たず、生成されそうになった瞬間にErgは停止する。
`Panic`という同じくインスタンスを持たない型が存在するが、正常に終了する際や意図的な無限ループの際は`Never`、異常終了する際には`Panic`を使う。
```erg
# Never <: Panic
f(): Panic = exit 0 # OK
g(): Never = panic() # TypeError
```
`Never`/`Panic`のOr型、例えば`T or Never``T`に変換することができる。これは、`Never`は意味論上起こり得ない(起こった場合プログラムは即時停止する)選択肢であるためである。
しかし、関数の戻り値型などで使用する場合、プログラムの終了が起こりうることを示すため`or Never`を省略することはできない。

View file

@ -0,0 +1,30 @@
# NonZero N
ゼロではない数を表すクラスです。ゼロ除算の安全性が保証されます。
```mermaid
classDiagram
class NonZero~Int~ {
...
}
class Int {
...
}
class Div {
<<trait>>
/(Self, R) -> O or Panic
}
class SafeDiv {
<<trait>>
/(Self, R) -> O
}
Int <|-- NonZero~Int~: Inherit
Div <|-- SafeDiv: Subsume
SafeDiv <|.. NonZero~Int~: Impl
Div <|.. Int: Impl
```
## methods
@Impl SafeDiv R, O
.`/`: Self.(R) -> O

View file

@ -0,0 +1,7 @@
# Object
全ての型の上位型である。
## methods
* __sizeof__: Nat

View file

@ -0,0 +1,7 @@
# Operator [...T], O
演算子の型です。
## def
Inherit Func [...T], O

View file

@ -0,0 +1,21 @@
# Option T = T or NoneType
「失敗するかもしれない」を表す型。
## methods
* unwrap(self, msg = "unwrapped a None value") -> T or Panic
中身が`T`型であると期待して取り出す。`None`であった場合`msg`を出力してパニックする。
```erg
x = "...".parse(Int).into(Option Int)
x.unwrap() # UnwrappingError: unwrapped a None value
x.unwrap("failed to convert from string to number") # UnwrappingError: failed to convert from string to number
```
* unwrap_or(self, else: T) -> T
* unwrap_or_exec(self, f: () -> T) -> T
* unwrap_or_exec!(self, p!: () => T) -> T

View file

@ -0,0 +1,8 @@
# Pos
Posは正数(1以上の整数)を表す型です。
0が入らないので、ゼロ除算の可能性を排除できるなどのメリットがあります。
## Def
`Pos = 1.._`

View file

@ -0,0 +1,5 @@
# Ratio
有理数を表す型です。主に、分数を使いたいときに使います。
実はErgでの/演算子はRatioを返します。1/3などは0.33333...とは評価されず1/3のまま処理されます。また、0.1は1/10と等価です。なので、`0.1 + 0.2 == 0.3`となります。当たり前のように聞こえますが、PythonではなんとFalseになります。
とはいえ、Ratio型はFloat型に比べて若干効率が下がる傾向があります。そこまで正確な数値は要らず、かつ実行速度が重要となるポイントではFloat型を使用するといいでしょう。とはいえ、Rob Pikeが言うように早すぎる最適化は諸悪の根源です。Ratio型を捨ててFloat型を使用するのは実際にパフォーマンステストを行ってからにしましょう。素人ほど無条件に軽量な型を好みます。

View file

@ -0,0 +1,14 @@
# Record
レコードの属するクラス。例えば`{i = 1}``Structural {i = Int}`型などの要素であり、`{i = Int}`クラスのインスタンスである。
他のクラスのインスタンスはレコード型の要素であってもレコードクラスのインスタンスではないことに注意。
```erg
assert not Structural({i = Int}) in Class
assert {i = Int} in Class
C = Class {i = Int}
c = C.new {i = 1}
assert c in Structural {i = Int}
assert not c in {i = Int}
```

View file

@ -0,0 +1,7 @@
# Result T, E
```erg
Result T, E <: Error = Either T, E
```
`Option`と同じく「失敗するかもしれない値」を表すが、失敗時のコンテクストを持てる。使い方は`Either`とほぼ同じである。

View file

@ -0,0 +1,3 @@
# StrWithLen! N: Nat! = Inherit StrWithLen N
可変長文字列を表す型。

View file

@ -0,0 +1,9 @@
# Str
(不変長)文字列を表す型。単なる`Str`型は`StrWithLen N`型から文字数の情報を落とした型である(`Str = StrWithLen _`)。
## methods
* isnumeric
文字列がアラビア数字であるかを返す。漢数字やその他の数字を表す文字の判定は`isunicodenumeric`を使う(この挙動はPythonと違うので注意)。

View file

View file

@ -0,0 +1,19 @@
# Subroutine
FuncやProcの基底型です。
## methods
* return
サブルーチンを中断して、指定した値を返す。ネストから一気に脱出する際に便利。
```erg
f x =
for 0..10, i ->
if i == 5:
do
f::return i
do
log i
```

View file

@ -0,0 +1,24 @@
# Tensor Shape: [Nat; N]
多次元配列を効率的に操作するためのクラス。多次元配列に対する積などの演算も定義する。
Matrix, Vectorなどはこの型を継承している。
```erg
Tensor.arange(0..9) # Tensor [10]
```
* reshape(self, NewShape: [Nat; M]) -> Self NewShape
```erg
(1..9).into(Tensor).reshape [3, 3]
```
* identity i: Nat -> Self shape: [Nat; N]
* zeros(Shape: [Nat; N]) -> Self
* ones(Shape: [Nat; N]) -> Self
* diag
* linspace
* logspace
* geomspace

Some files were not shown because too many files have changed in this diff Show more