神話のプログラム言語 Odin(これであなたも厨二病)
Odin
と言うプログラム言語を、ご存じでしょうか?
最近、youtubeで海外のコンピューターサイエンティストの間でも、話題として取り上げられるようになってきました。
Odin
は、C言語の代替プログラム言語とも言われ、ジェネリクス、パターンマッチング、エラー処理など、現代的なプログラミング言語の機能を備えたプログラム言語だと言われています。
C言語の代替プログラム言語といえば、CarbonやRustが有名ですが、どちらの言語も難解すぎると言う欠点があります。
Odin
の開発者はスウェーデンのgingerBill氏で、Odinと言う言葉は、北欧神話に登場する神の名前です。知識と詩の王であり、死と復活を司る神として知られています。
神話の神を司った、このプログラム言語を覚える事で、あなたも神に・・・。(厨二病)
失礼、あなたもC言語を脱却出来るはずです。
最近では、JangaFX社のリアルタイム流体シミュレーションツールEmberGenでOdin
を使用しているらしいです。
※このEmberGenというソフト、噂では一人の人物だけで作られたと言われていますが、gingerBill氏なんでしょうかね?
基本、私はNim言語が大好きで、Nim言語に関する記事しか書きません。
理由は、プログラム記述がPython並みに読みやすく、C言語並みに処理速度が速いため、
プログラムを書いても楽しく、他の人が見ても理解しやすい言語だからです。
楽しくもないプログラム言語で、プログラムを書く事は、苦痛以外の何物でも無いと思っています。
じゃあ、Odinはどうかと言えば、私が理想としてるC言語です、コードを書いてても、中々楽しい言語だと思って記載してみました。今後はNimとOdinの記事を書いて行くと思います。
マイナーな言語が好きなんですね?って、それは違います!。先見の明があると言ってください。(笑)
どうせ、C言語に変換される言語なら、直接C言語を書けば良いのでは?と思われますが、私がC言語を嫌ってる理由は以下の通りです。
- ヘッダーとソースを分けての記述が面倒
- ifdefが嫌い(ソースの処理中にifdefが入ると読みづらい)
- Makefileが腐ってる(50年も前のmakeを未だに使っている)
- ベンダーによってコンパイルオプションが異なる
- 標準ライブラリがしょぼい
Odin言語の特徴
では、Odin
の特徴を見ていきましょう。
- 手動によるメモリ管理
- 例外処理がない
- パッケージマネージャーがない
- クラスがない
- deferが使える
- struct union enumが扱える
- ポインターが扱える
- FFIでC言語が扱える
- ジェネリックスが使える
ない物尽くしの言語ですが、そもそもC言語も、元は何もないプログラム言語です。
Odin
の設計理念は、必要な機能を最低限提供しながら、C言語と同じくらいシンプルに扱えるところです。
また、私がOdin
を気に入ったのは、Raylib作者のCentralized Memory Managementの記事を読んだからです。プログラムによる集中メモリ管理。(トラッキングによる未開放メモリの調査や、どこでメモリを使って、どこで解放したかを管理する。良いじゃないですか!)
もし、他のシステム言語(RustやZigなど)で言語の複雑さに不満を持つ人にとっては、Odin言語
は非常に魅力的な言語だと思います。
環境
まず、Odin
を動かすには、以下のアプリが必要になります。
私のPC環境はWindowsで動作させています。
- OS: Windows11
- Odin dev-2024-10-nightly
- Microsoft visual C++ 2022
Odin言語の導入
下記の導入手順は、Windows環境での手順になります。MacやLinuxであれば、別途、Odin installを確認下さい。
1. Microsoft Visual C++をダウンロード
Windows環境のOdinは、Visual C++でコンパイル・実行されるため、まずはMicrosoftのダウンロードサイトから、Visual C++をダウンロードします。
2. Odinをダウンロード
Visual C++のインストール後に、Odinリリースサイトから最新のアーカイブをダウンロードします。私の場合は、Windows環境なので、「odin-windows-amd64-dev-2024-10.zip」をダウンロードします。
3. 環境変数の設定
ダウンロードしたアーカイブファイルを、設定したいディレクトリに移動し、解凍します。
その後、環境変数のPATHに設定したディレクトリを追加するだけです。
4. 動作確認
DOSプロンプトを開き、「odin version」を実行します。パスが通っていれば、「odin.exe version dev-2024-10-nightly」と表示されるはずです。
5. VSCodeの拡張機能を設定
VSCodeを開き、拡張機能の検索から「odin」と入力して検索します。
複数存在しますが、「Odin Language」を選択し、インストールを行います。
Odinプログラムを書いてみる
1. はじめてのOdinプログラム
VSCodeから、新規のプロジェクトを開き、「lesson01」ディレクトリを作成します。
次に、以下のmain.odinファイルを作成します。
※ファイル名は特にmain.odinする必要はありません、main関数が含まれてるとわかるようにしているだけです。
/* ===========================
はじめてのOdinプログラム
=========================== */
package main // 自身のパッケージ名
import "core:fmt" // odinの標準ライブラリ指定はcore:から始まる
main :: proc() { // main処理:プロシジャー宣言は::から右位置に記載
fmt.println("Hellope!") // C言語で言うところのprintf (処理後のセミコロンは不要)
}
VSCodeのPowerShellから実行
$ odin run .\lesson01\
Hellope!
2. 関数の戻り値を得るOdinプログラム
プロジェクト直下に、「lesson02」ディレクトリを作成します。
次に、以下のmain.odinファイルを作成します。
/* ===========================
Odinプログラム 関数呼び出し
=========================== */
package main // 自身のパッケージ名
import "core:fmt" // 参照ライブラリの指定
import "core:strings"
import "core:sys/windows"
main :: proc() { // main処理
// 古いpowershellだと文字化けを起こすため、出力文字をUTF8に宣言
when ODIN_OS == .Windows {
windows.SetConsoleOutputCP(windows.CODEPAGE.UTF8)
}
fmt.println("日本語おっけー") // 日本語表示
fmt.printf("int rtn=%v\n", rtn_int(2, 3)) // %vはvalueの略式名
fmt.printf("string rtn=%v\n", rtn_str("あいう", "かきく"))
}
rtn_int :: proc(a, b : int) -> int { // 数値を返す関数
return a * b
}
rtn_str :: proc(a, b : string) -> string { // 文字列を返す関数
new_name := strings.concatenate({a, b})
defer delete(new_name) // 抜けた後にメモリを解放
return new_name
}
日本語文字化けに関しては、issuesリストにありましたが、powershellを最新にするか、上記の様に宣言すれば表示可能です。
ただ、これはバグでは無いらしいですが、Nim言語ではpowershellを最新にしなくても、日本語は問題なく表示されているので、Nimは対応しているって事なんでしょうね。
$ odin run .\lesson02\
日本語おっけー
int rtn=6
string rtn=あいうかきく
3. パッケージの参照
Odinでのimport参照について説明します。
基本、ディレクトリ名=パッケージ名だと思って結構です。
ですので、下記の様にlesson03の下にmiddleディレクトリを作成し、その下にbottomディレクトリを作成し、参照モジュールを配置します。
lesson03 + main.odin
+ middle + middle01.odin
+ middle02.odin
+ bottom + bottom01.odin
lesson03ディレクトリ内のmainソースは以下の通り
/* ===========================
Odinプログラム packageからの呼び出し
=========================== */
package main // 自身のパッケージ名
import "core:fmt" // 参照ライブラリの指定
import md "middle" // importの省略名は左に記載可能
import "middle/bottom"
main :: proc() { // main処理
fmt.println("main")
md.sub01()
md.sub02()
bottom.sub_sub01()
}
middleディレクトリ内のmiddle01.odinソースは以下の通り
package middle // パッケージ名はディレクトリ名と同等
import "core:fmt"
sub01 :: proc() {
fmt.println("middle->middle01")
}
middleディレクトリ内のmiddle02.odinソースは以下の通り
package middle // パッケージ名はディレクトリ名と同等
import "core:fmt"
sub02 :: proc() {
sub03()
}
@private sub03 :: proc() { // package内であれば呼び出せる
fmt.println("middle->middle02")
}
bottomディレクトリ内のbottom01.odinソースは以下の通り
package bottom // パッケージ名はディレクトリ名と同等
import "core:fmt"
sub_sub01 :: proc() {
fmt.println("bottom->bottom01")
}
$ odin run .\lesson03\
main
middle->middle01
middle->middle02
bottom->bottom01
4. 型定義
Odinでの型定義と配列演算について説明します。
型は色々あるので、全ての型を知りたければ、ドキュメントを参照してください。
/* ===========================
Odinプログラム 型定義
=========================== */
package main // 自身のパッケージ名
import "core:fmt" // 参照ライブラリの指定
main :: proc() { // main処理
// --- 整数型
i01: int // 変数は宣言された時点で0に初期化される
i01 = 7 // i01: int = 7 でも同じ またはi01 := 7も可能
// --- 文字列型
s01 := "Hello World!"
fmt.println( type_info_of(type_of(s01)) ) // 型名称を返す
// --- Bit型
b01: byte = 0b0110_1011 // byte指定しないとint型になる
fmt.printf("b01 type=%v value=%v hex=%#02x\n", type_info_of(type_of(b01)), b01, b01)
// --- struct型
Vec3 :: struct {
x, y, z: int
}
vec3 := Vec3 {x = 1, y = 2, z = 3} // 宣言と初期値を同時に設定
fmt.printf("vec3 type=%v value=%v\n", type_info_of(type_of(vec3)), vec3)
// --- 配列
// 配列でも演算は可能。また、勝手にxyzの変数まで付けてくれる
arr01 := [3]int {4, 5, 6} // 宣言と初期値を同時に設定
fmt.printf("array %v + 2 = %v\n",arr01, arr01 + 2) // 配列演算
fmt.printf("arr01 x=%v y=%v array zx=%v\n", arr01.x, arr01.y, arr01.zx)
// --- ハッシュマップ
some_map := map[string]int{"A" = 1, "C" = 9, "B" = 4}
defer delete(some_map) // 関数の最後にメモリ解放
for key in some_map {
fmt.println(key)
}
}
配列については、pythonのnumpyのように単一演算と同列配列での演算が可能です。
$ odin run .\lesson04\
string
b01 type=u8 value=107 hex=0x6b
vec3 type=Vec3 value=Vec3{x = 1, y = 2, z = 3}
array [4, 5, 6] + 2 = [6, 7, 8]
arr01 x=4 y=5 array zx=[6, 4]
A
C
B
5. 関数の合成
一つの関数を呼んでると見せかけて、実は異なる関数を呼び出している。
/* ===========================
Odinプログラム 関数の合成
=========================== */
package main // 自身のパッケージ名
import "core:fmt" // 参照ライブラリの指定
main :: proc() { // main処理
// 同一の関数呼び出しですが、引数によって呼び出す関数が異なる
rtn1 := read_main(211) // read_b関数を呼び出している
rtn2 := read_main("test", "aaa") // read_a関数を呼び出している
rtn3 := read_main(true) // read_c関数を呼び出している
}
read_a :: proc(a, b: string) -> int {
fmt.printf("read_a = %v\n", a)
fmt.printf("read_a = %v\n", b)
return 300
}
read_b :: proc(id: int) -> int {
fmt.printf("read_b = %v\n", id)
return 0
}
read_c :: proc(b: bool) -> int {
fmt.printf("read_c = %v\n", b)
return 100
}
// プロシジャーの塊。戻り値は合わせないとバグの元になる
read_main :: proc {
read_a,
read_b,
read_c,
}
$ odin run .\lesson05\
read_b = 211
read_a = test
read_a = aaa
read_c = true
おわりに
Odin
は2017年頃から開発された言語ですが、未だにBeta版扱いのままです。
理由は、神であるため人間のお金がないので、プログラム実装が遅れているのが原因です。
今フルタイムで開発を行っているのは、神のみだそうです。
Nimでさえフルタイムで開発作業を行っているのは二名と聞いています。
流石に、大手企業がスポンサー契約してもらえれば良いのですが、神と対等に話せる人間が居ないのでしょうかね?(笑)
企業が金をかけて、大々的にプロモーションすれば、それが良いプログラム言語だとは思いませんが、
Googleが開発したCarbonは2年経って、全然話は聞かなくなりました(やる気ないんでしょう)、
Modular社のMojoに関しては、Mojo言語がまだ安定さえしていないのに、MAXと言う製品を販売すると言う荒技を行っています。
個人的には、どちらも失敗する可能性は高いと思っています。
次回は、Odin
のメモリーアロケーターについて、記事を書いていけたらと思ってます。
Discussion