世界を動かす技術を、日本語で。

Nim 2のレビュー:良い点と悪い点、サンプルコード付き

概要

  • Nim 2 以降、 ORC/ARC がデフォルトのメモリ管理方式
  • C++ライクなRAII やリファレンスカウント、豊富なC++インターフェース
  • 柔軟かつ高性能 なシステム言語で、 Python風の書きやすさ も特徴
  • メタプログラミング多様なバックエンド 対応など、開発の自由度が高い
  • ツールチェーンやLSP など、改善の余地も存在

Nim 2の魅力と現状

  • Nim は1-2年使ってきたが、 過小評価 されがちな言語
  • Nim 2 からは トレースGC が非推奨、 ORC/ARC による C++風RAII が標準
  • ref型 はC++の shared_ptr のようなリファレンスカウント(デフォルトは非アトミック、 --mm:atomicArc でアトミック化可能)
  • Nim 3(Nimony) では atomicArc がデフォルト化予定
  • Carbon language の代替としても実用可能な 生産性実用性
  • C++との高い互換性 (テンプレート、コンストラクタ、デストラクタ、演算子オーバーロードなど)
  • ただし、 C/C++の可読性の高いコード生成 は目指していない点は Carbon と異なる
  • NIF (Lisp風の中間表現)導入で インクリメンタルコンパイルツール改善 を推進中

Nimの特徴と強み

  • 簡潔性Python のような書きやすさ、 最小限のボイラープレート
  • 柔軟性メタプログラミング多様なバックエンド (C/C++/Objective-C/JavaScript)対応
  • 高性能C/C++/Rust などのシステム言語と同等のパフォーマンス
  • RAIIサポートデストラクタ/ムーブ/コピー 対応
  • 手動メモリ管理 も可能( defer--mm:none 利用)
  • クロスコンパイルC/C++/ObjC/JS 向けに出力可能、任意のC/C++コンパイラ利用可
    • importc/importcpp/importjs で既存ライブラリ利用
    • C++テンプレート/メンバ関数/演算子 等もサポート
    • NimScript によるスクリプト的な利用やビルド設定も可能

言語設計と型システム

  • UFCS (Uniform Call Syntax)で プロパティ/メソッド/演算子 を柔軟に記述
  • assert/doAssertアサート指向プログラミング が可能
  • 型システム :バリアント、ディスティンクト型、タプル、enumビットセット
  • ジェネリクス/型クラス/コンセプト による柔軟な型制約
  • イテレータyield による独自データ構造の簡単な反復
  • result変数 の自動用意で QoL向上
  • asyncマクロ によるAST変換で実装( asyncdispatch/chronos エンジンあり)

メタプログラミング

  • 型関係式when文 によるコンパイル時分岐
  • fieldPairsプロシージャオーバーロードシリアライズ/デシリアライズ が容易
  • カスタムプラグマ でフィールドへのメタデータ付与
  • 任意のコンパイル時コード実行 (例: const myFileContents = readFile("file.txt")
  • マクロ でAST変換、 テンプレート で安全なコード展開
    • テンプレートはハイジーン的 (スコープ汚染防止)

シンプルなKey/Valueファイルフォーマットの実装例

  • fieldPairsプロシージャオーバーロード の組み合わせで、 任意型のロード/パース を実現

  • 例:Config型の定義と、対応するテキストファイル

    type Config = object
      name: string
      lr: float
      betas: seq[float]
    
    name=my experiment
    lr=0.001
    betas=[0.99, 0.999]
    
  • parseValue のオーバーロードで型ごとにパース処理を記述

  • load[T] 関数でファイルを読み込み、 型T のインスタンスを生成

  • seq[T] 型への対応も parseValue の追加オーバーロードで実装

  • 実行時 にも コンパイル時 にもロード可能(let/constの使い分け)

Nimの課題・改善点

  • LSP(Language Server Protocol) の速度や安定性に課題
  • ツールチェーンコンパイラオプション の理解が必要
  • 細かい不満点 はあるものの、 生産性を大きく阻害しない

まとめ

  • Nim 2現代的なメモリ管理高い柔軟性 を持つシステムプログラミング言語
  • C++やRust、Carbon の代替としても検討可能
  • メタプログラミング多様なバックエンド簡潔な記法 で開発効率を大幅に向上
  • ツール面 にはまだ課題が残るが、 言語本体の完成度は高い
  • 実用例 を通して、 Nimの特徴的な設計思想 を体感可能

Hackerたちの意見

これはまあまあの概要だけど、いくつかの良い点が抜けてるね。興味がある人は、これが全てだと思わない方がいいよ(一般的に、そう思わない方がいいけど)。例えば、その機能がすごく珍しいから(物議を醸す?)あまり言及されないけど、Nimでは自分で演算子を定義することもできるんだ。だから、C系のプログラミング言語からビット演算の |= が missing だと思ったら、こう書けばいいよ:proc |=*[T,U](a: var T, b: U) = a = a or b。もちろん、Nimにはビットセットを管理するための set[T] があって、交差や和などの伝統的な集合論的演算子を使うと、もっと便利に管理できるよ。例えば、https://github.com/c-blake/procs は set[enum] を使って、どの Linux /proc ファイルを読み込むか、どのフィールドを解析するかの依存関係分析をしてるんだ(Cの procps の代替よりもずっと速いしね)。このユーザー定義の演算子の記法は、テンプレートやマクロにも使えるから、カスタマイズされた記法やドメイン特化型言語(DSL)を作るのがすごく簡単になるよ。それに、プラグママクロを使えば、特別なコンパイル時の動作を定義にタグ付けするのも簡単だしね。まあ、そんな感じ。

興味がある人のために、優先順位は演算子の名前に基づいているよ。https://nim-lang.org/docs/manual.html#syntax-precedence

それいいね!たまにCで恋しくなるのが||=なんだよね。ビット演算子には|=があるのに、ショートサーキットがないから全然違うのに混同しちゃいけないんだよね。

そうだね、もうちょっとわかりやすく書けばよかったかな。でも、「これはNim 2の機能の一部」って言ったし、Nimのマニュアルのコピペになりたくなかったんだ。投稿はすでに結構長いから、自分が偏っている機能を優先することにしたんだよね。それに、言語設計の最初のポイントでオペレーターのオーバーロードについても触れたけど、もっと強調すればよかったかも。

Nimの一番クールなところは、マクロが型システムやオーバーロード解決に参加して、型チェックされたコードとされていないコードの両方で動くところだね。

このテンプレートやマクロの型ディスパッチの「二重性」は、スコープルールがテンプレートやマクロを使って、サブスコープ内だけで「有効」なものを定義できることも意味してるんだ。例えば、古いCのポインタ演算の危険性みたいにね - 「定義されてるけど含まれてる」ってやつ。https://forum.nim-lang.com/t/1188#7366 ちょっとしたところで、Nimは静的型付けのLispに、ややPythonっぽい表面構文を持った感じだね。ただ、これだけじゃNimコードを書くときの選択肢の多さを十分に評価できてないけど。

これが俺を実際にNimを試してみようと思わせるコメントだわ。

WASMは標準ライブラリではサポートされていません。Cの出力を使ってemccを使えば、この問題は解決できる?

そうそう - https://github.com/treeform/nim_emscripten_tutorial ちなみに、emscriptenが出てからずっと人々はそうしてきたけど、この記事は標準ライブラリや標準コンパイルツールチェーンとのより密な統合が欠けてることを指摘してるね。一般的に、標準ライブラリを進化させるのは大変だと思う。でも、言語とコンパイラはほとんどのものよりも柔軟だから、Nimではそれほど重要じゃないかもね。

はっきり言うと、emscriptenを使ってWASMにコンパイルできるよ。ただし、ブラウザのWASMからDOMとやり取りするためにJavaScriptコードを呼び出したい場合、標準ライブラリで宣言されたバインディングは使えないんだ。自分でバインディングを書く必要があるよ。ここに、emscriptenを使ったNimの例のリポジトリがあるよ: https://github.com/miguelmartin75/nim-wasm-experiments。あと、websockets用のemscriptenのC APIへのバインディングもここにあるよ: https://github.com/miguelmartin75/nim-wasm-experiments/blob/...。emscriptenを使えば、NimでEM_JSマクロを使ってJavaScriptへのバインディングを作成できるよ。やり方の例はここにある: https://github.com/treeform/windy/blob/bc98d4642c700f0277551...

実際、Nimは今後のCarbon言語の生産準備が整った代替として使えるかもしれません。NimはC++との素晴らしい相互運用性を持っていて、テンプレートやコンストラクタ、デストラクタ、オーバーロードされた演算子などをサポートしています。ただし、Carbonの目標とは異なり、読みやすいCやC++コードにはコンパイルしません。まあ、Carbonに興味を持つ理由によるけど、最後の文で少しほのめかされてるね。私の理解では、大きな目標はGoogleの大規模なC++コードベースをより健全な言語に自動移行できるようにすることだと思う。Mondがそれについていいブログ記事を書いてたよ[0]。Nimはそれじゃないけど、もちろんCarbonもまだそうじゃないし、どこまで行くかは見てみないとわからないね。Carbonを応援してるけど、いいアイデアだと思う。とにかく、既存のC++コードを変更せずに使える後継言語を探すのとは違う野望だね。ここでNimが得意とされていることだよ。 [0] https://herecomesthemoon.net/2025/02/carbon-is-not-a-languag...

C++との「素晴らしい相互運用性」についてはちょっと懐疑的だな。もしそんなに簡単なら、Rustの人たちがもうやってるはずだし、まだ模索してるみたいだしね。RustはC++と共有されているLLVM用に開発されてるし。

Hacker Newsで議論の続きを見る