**********************************************************************
セッションS5-b
テーマ:Nimです、こんばんは
コーディネータ: 浅田睦葉 (筑波大学)、細合晋太郎 (東京大学)
日時:2022/9/2(金) 14:00-15:10
参加人数:8人 (開始時 オンライン)
**********************************************************************
■セッションの概要説明
プログラミング言語Nimについて
基本文法からマクロまで
■Nimとは
Andreas Rumpf氏が2008年から開発を続けているシステムプログラミング言語
複数の言語をバックエンド言語に持つ静的型付けのコンパイラ言語
コードはPythonライクなインデントベース
■Nimでできること
基本何でもできるが、ここでは5つ挙げる。
・Web開発: Javascriptと好相性でフロントエンド、バックエンドどちらもフレームワークが充実。
・CLIツール: cligenというオプション解析ライブラリが非常に優秀。メタプログラミングの恩恵を受けている。
・DevOps: NimのサブセットNimScriptが実行仮想環境であるNimVM上で動作し、クロスプラットフォームに対応。
・OS開発: ガベージコレクタを持ち、生ポインタ操作可能で、スタンドアロンなバイナリを生成できる。
・組込み開発: これまで色んな実験がされており、バックエンドにデフォルトでC99を出力するため導入しやすい。
■実行環境
nim playgroundが提供されており、オンラインでコンパイル、実行可能。
■基本文法
プロシージャ:
他言語における関数/メソッド/手続き。result変数が特徴的で、一時変数を用意しなくても直接戻り値を構成できる。
例えば動的配列を返す場合に便利。第一級オブジェクト。
UFCS(統一関数呼び出し構文): 元々はD言語が持つ構文で、クラスが無く、関数呼び出しとメソッド呼び出しを同一視できる。
seq[T]:
動的配列型。実行時、配列サイズ変更可能。要素が増えるたびにポインタを確保する。Tは型引数で任意の型を入れられ、
seq[T]型はジェネリクスと呼ばれる抽象型である。
array[I; T]: 静的配列型。コンパイル時に配列サイズが決定。Iは型引数で、整数定数の配列サイズ。
条件分岐
if文/if式: 厳密にbool値のみを受け取る。式の場合は必ず値を返す。
when文/when式: コンパイル時における条件分岐構文。条件を満たすブロックのみコンパイルする。式の場合は必ず値を返す。
for文: Nimにおいて最も基礎的なループ文。イテレータから値を取り出して識別子に代入し手実行、イテレータから値を取り出し終えると終了。
イテレータ: ある集合構造を繰り返し、値を取り出すことができるインターフェース。第一級オブジェクト。
■モジュールとパッケージ
importで読み込む。Nimは洗練されたモジュールシステムとパッケージディレクトリを持つ。
モジュールシステムによりプログラムを機能分割できる。情報の隠蔽やファイルの個別コンパイルが可能。
アスタリスク (*) でマークすることで他のモジュールに公開し、パブリックになる。そうでないシンボルは非公開となる。
except節によって特定のシンボルを除く。
from節によって特定のシンボルのみをインポート。このときnilをインポートすると接頭辞を強制することができる。
Nimbleを用いて複数のモジュールをまとめてパッケージ化し、他者に配布可能。Nimbleパッケージはnimbleコマンドによってビルド/実行できる。
Nimble: ビルドツール兼パッケージマネージャ。公式が出している。
Nimbleファイル: パッケージのメタデータ、依存パッケージとそのバージョン、タスクなどを記述する。NimScriptが記述できる。
■Nimの型
符号付き整数: int/8/16/32/64型
符号なし整数: uint/8/16/32/64型
浮動小数点数: float/32/64型
真偽値(bool型):
関係演算子: <,<= >,>= ==,!=
論理演算子: and or xor not
文字型(文字列)型
列挙型
部分範囲型: subrange型と呼ばれ、序数を持つ型の範囲を定めて新しく型を定義できる。
構造体型: デフォルトではディープコピーされ、refを付けることでシャローコピーできる。
■プラグマ
コンパイラに補足情報や実行命令を与える。メタプログラミングにおける特殊な命令を実行でき、出力されるCプログラムに対して干渉できる。
■FFI
C/C++/Objective-C/JavaScriptとは相互運用可能。CコードをNimで実行するには、対象ファイルの絶対/相対パスを渡す
headerプラグマを使う。これはソースコードファイル/共有ヘッダからシンボルを読み込める。逆にNimコードをCで実行することも可能。
■ARC/ORC
ARC: Nimにおける決定論的な参照カウンタ
ORC: ARCベースの循環参照コレクタ(非決定論的)
Nimはガベージコレクタ(GC)を持つ影響で、実行時にメモリが自動的に回収されてしまうことによって、その挙動が追いづらい。
そこで、GCっぽい挙動を残しつつコンパイル時に解決しようという手段のARCがある。ARCは参照カウンタのため、循環参照を処理できないが、
ARCベースの循環参照を収集できるORCが開発されている。こちらは現在実験段階であり、将来的にはGCをORCに移すことが検討されている。
■ジェネリクス
型引数を使った抽象的なシンボル定義をまとめており、型やプロシージャに対して同様の抽象化を行うことができる。
■テンプレート
衛生的な仕組みを兼ね備える抽象構文木の置換メカニズム
Cにもプリプロセッサで置換するようなマクロがあるが、これはスコープを持たないため、開発者が未想定のコードに置換され、
エラーが追えなくなってしまうということが起こりうる。
テンプレートはスコープを備えるかつ、意味解析の前に行われるが、構文木にパースできるかどうかをコンパイル時に考えられるようになっている。
インラインプラグとの違い
テンプレート: 意味解析時に展開される
インラインプラグマ: インライン関数指定 (__inline) をして出力するがインライン化するかどうかはバックエンド言語に依存する
テンプレートはグローバルに公開されるプログラムとスコープを持つプログラムを分けて生成する。
■マクロ
抽象構文木を受け取り、抽象構文木を返す言語機能
コンパイル時実行(CTFE)をサポートし、一部制限はあるが、プロシージャ、イテレータ、テンプレート、マクロをコンパイル時に実行できる。
expandMacrosマクロを用いて展開構造を確認する。
■まとめ
NimはC/C++/Objactive-C/JavaScriptをバックエンドに持つコンパイル言語である。
静的型付け言語であり、ガベージコレクタ(GC)を持つ。
Web開発から組込み開発まで、幅広く手適用できる。
洗練されたFFIにより、非常に簡単に上記の各言語と相互運用が可能。
<質疑応答>
・質問
列挙型の各バリアントに値を持たせられないのでしょうか?
・回答
できない。
・質問
ディープコピー・シャローコピー・生ポインタの説明を頂きましたが、同じ構造の構造体を違うコピー方法で使いたい場合、
それぞれを別の型として宣言する必要が基本的にあるという理解でよろしいでしょうか?
・回答
そうではなく、例えば、何か値を持っていてそれをプロシージャに渡す場合、明示的制御方法としてプロシージャにシャローコピー/ディープコピーに関する
プラグマを与えると、そのプロシージャは常に、値で受け取るか、参照で受け取るかを切り替えられる。2つ目の方法として、変数定義時に使うvarという
キーワードを引数に対して付けた状態で変更すると、呼び出し元にも適用される。varを付けない場合、呼び出し元には影響がない(コピーされただけ)。
・質問
P.70のデストラクタの挿入は、コンパイルしてCなど別の言語を生成?するときに挿入するのでしょうか?それとも、デストラクタ挿入などコンパイル時に
操作を加えるnimコードを別に生成して、それを別言語にコンパイルするのでしょうか?
・回答
前者。コンパイルされるときに挿入される。JavaScriptはClientJSに書き出すことが今は中止になっており、かなり制約が強く、
メモリ管理関係のプログラムはJSにコンパイルできない。他方で、C/C++/ObjectiveCには大概のメモリ管理は効くことになっている。
・コメント
C++, Kotlin, Haskell、Python、Typescript、 Rust,,,,みたいな超いいとこどりしてるのに、
全然ごちゃごちゃも破綻もしてないのがすごいバランスですよね。VMや実行時の誤魔化しなしに実現してるのもすごい。
・コメント
メタプログラミングで推す言語という印象を持ちました。マクロに項書換系が入ってるのは見たことがないです。