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

Nanolang: コーディングLLMを対象とするために設計された小さな実験言語

概要

NanoLangは、 シンプル かつ LLMフレンドリー なプログラミング言語。 Cへの トランスパイル によるネイティブ性能と 明快な構文 を両立。 全関数に 必須テスト を要求し、AIによるコード生成にも最適化。 自己ホスティング や自動依存管理など、現代的な機能を搭載。 主要OSで動作し、学習・貢献も容易な設計。

NanoLang概要

  • NanoLang は、最小限かつ AIに優しい構文 を持つプログラミング言語
  • C言語へのトランスパイル により、ネイティブ並みの高速実行を実現
  • 全関数に必須のシャドウテスト を導入し、品質担保
  • 自己ホスティング (Stage 0 → Stage 1 → Stage 2ブートストラップ)をサポート
  • 明示的な構文前置記法 により、AIや人間にも誤解のないコード生成

インストールとクイックスタート

  • GitHubリポジトリ からクローン: git clone https://github.com/jordanhubbard/nanolang.git
  • ディレクトリ移動:cd nanolang
  • ビルド:make build
    • bin/nanoc:NanoLangコンパイラ(Cコードへトランスパイル)

Hello World例

  • hello.nanoファイル作成:

    fn greet(name: string) -> string {
      return (+ "Hello, " name)
    }
    shadow greet {
      assert (str_equals (greet "World") "Hello, World")
    }
    fn main() -> int {
      (println (greet "World"))
      return 0
    }
    shadow main { assert true }
    
  • ビルド&実行:

    • ./bin/nanoc hello.nano -o hello
    • ./hello

プラットフォームサポート

  • Tier 1(完全サポート)

    • Ubuntu 22.04+(x86_64)
    • Ubuntu 24.04(ARM64:Raspberry Pi, AWS Graviton等)
    • macOS 14+(Apple Silicon)
    • FreeBSD
  • Tier 2(WSL経由でサポート)

    • Windows 10/11(WSL2で動作)

      • PowerShellでwsl --install -d Ubuntu
      • WSL内で通常のインストール手順
    • 理由 :NanoLangの依存(SDL2, ncurses, pkg-config)はUnix/POSIXライブラリ

    • 注意 :現時点でWindowsネイティブバイナリ(.exe)は未対応

  • Tier 3(実験的)

    • macOS Intel(Rosetta 2経由または旧Macでネイティブ)
    • その他Linux(Arch, Fedora, Debian等)
    • OpenBSD(依存手動インストール)

主な特徴

  • 前置記法 :演算子の優先順位なし、常に明確な構文
  • 必須テスト :全関数にシャドウテストブロック必須
  • 静的型付け :コンパイル時型チェック
  • ジェネリック型 :Result<T, E>等によるエラーハンドリング
  • C言語インターフェース :FFIサポート&自動依存管理
  • 標準ライブラリ :Result<T, E>、文字列操作、数学等
  • モジュールシステム :module.jsonによる自動依存管理

ドキュメントと学習パス

  • User Guide(HTML) :段階的チュートリアル+実行可能スニペット
  • Getting Started :15分で学べるチュートリアル
  • Quick Reference :構文チートシート
  • Language Specification :完全リファレンス
  • Examples :全機能を網羅した動作例

言語構文・型システム

  • 変数宣言 :let x: int = 42(デフォルトimmutable)、let mutでmutable
  • 関数定義 :fn add(a: int, b: int) -> int { ... }
  • 必須テスト :shadow add { assert (== (add 2 3) 5) }
  • 制御構文 :if/else, while
  • ループ例 :while (< i 10) { print i set i (+ i 1) }
    • プリミティブ:int, float, bool, string, void
    • 構造体:struct Point { x: int, y: int }
    • 列挙型:enum Status { Pending = 0, Active = 1, Complete = 2 }
    • ジェネリックリスト:let numbers: List<int> = (List_int_new)
    • 第一級関数:let f: fn(int) -> int = double
    • ジェネリックユニオン:union Result<T, E> { Ok { value: T }, Err { error: E } }

標準ライブラリ例

  • Result型によるエラーハンドリング

    union Result<T, E> { Ok { value: T }, Err { error: E } }
    fn divide(a: int, b: int) -> Result<int, string> {
      if (== b 0) { return Result.Err { error: "Division by zero" } }
      return Result.Ok { value: (/ a b) }
    }
    fn main() -> int {
      let result: Result<int, string> = (divide 10 2)
      match result {
        Ok(v) => (println v.value),
        Err(e) => (println e.error)
      }
      return 0
    }
    

代表的なサンプル

  • コア例 :hello.nano, calculator.nano, factorial.nano, fibonacci.nano, primes.nano
  • ゲーム例 :snake_ncurses.nano, game_of_life_ncurses.nano, asteroids_complete.nano, checkers.nano, boids_sdl.nano
  • examples/README.md で全リスト確認可能

モジュールと自動依存管理

  • グラフィックス・ゲーム用
    • ncurses(ターミナルUI)、sdl(2Dグラフィックス)、sdl_mixer(音声)、sdl_ttf(フォント)、glfw(OpenGL)
    • 初回利用時にHomebrewやapt等で自動インストール
    • 詳細:docs/MODULE_SYSTEM.md

ビルド&テスト

  • ビルド :make build(3段階ブートストラップ)
  • 全テスト :make test
  • クイックテスト :make test-quick
  • 全サンプルビルド :make examples
  • 例ブラウザ起動 :make examples-launcher
  • ユーザーガイド検証 :make userguide-check
  • 静的HTML生成 :make userguide-html
  • ローカルサーブ :make -C userguide serve
  • クリーンビルド :make clean
  • インストール :sudo make install(PREFIX指定可)
  • BSD系 :gmake build, gmake test等

LLMへのNanoLang教育

  • LLM向け設計 :曖昧さのない構文+必須テストでAIの学習効率向上
  • MEMORY.md :構文、パターン、デバッグ手法、よくあるエラー等を網羅
  • spec.json :公式な型・標準ライブラリ・構文仕様
  • examples/ :全機能の実用例
  • 推奨学習順
    • MEMORY.mdで実践知識習得
    • spec.jsonで形式仕様確認
    • examplesでイディオム把握

貢献方法

  • 貢献歓迎 :例追加、ドキュメント改善、バグ報告・機能提案、新モジュール・標準関数実装
  • ガイドライン :CONTRIBUTING.md参照

プロジェクトステータス

  • 現状 :自己ホスティング対応の本番運用コンパイラ
  • 実装済み機能
    • 完全な言語実装(字句解析・構文解析・型検査・トランスパイル)
    • Cトランスパイルによる高速化
    • 型推論付き静的型付け
    • 構造体・列挙型・ユニオン・ジェネリクス
    • モジュール自動依存管理
    • 49以上の標準関数
    • 90以上の動作例
    • シャドウテストフレームワーク
    • CライブラリFFI
    • メモリ安全機能
  • 今後の計画 :docs/ROADMAP.md参照

NanoLangの意義

  • AIコード生成 :曖昧さのない構文でAIの誤りを低減
  • テスト重視 :必須テストで品質向上
  • シンプル&高速 :最小限のキーワード・構文、C由来の高性能

設計思想

  • 最小限の構文 (キーワード18個、Cは32個)
  • 唯一明快な方法 で記述
  • テストを言語仕様に組み込み、後付けではない
  • Cトランスパイル で最大互換性

ライセンス・リンク

  • ライセンス :Apache License 2.0(LICENSE参照)
  • ドキュメント :docs/
  • サンプル :examples/
  • イシュー :GitHub Issues
  • 貢献ガイド :CONTRIBUTING.md

Hackerたちの意見

ここでの新しいポイントは、すべての関数にコンパイル時に実行されるテストが必要だってこと。カスタム言語をLLMに教える価値があるのか、LuaやPythonみたいな既存の言語を使ってテスト要件を適用する方がいいんじゃないかって、まだ疑問に思ってる。

PyretっていうCSのための教育用言語は、Racketみたいな感じで、関数を書くことでテストが必要なんだよね。 https://pyret.org/docs/latest/testing.html

それが新しいかどうかはわからないけど、例でないものに関しては信号対ノイズ比がどうなるか疑問だな。実際のソースコードのファイルは、テストによって完全に汚染されるか(テストは実際のコードよりもずっと長いから)、fn process_order { ... }のような影のprocess_order { assert test_process_order }になって、テストコードは別のファイルに書かれ、テストコード内のすべての関数には真を主張する影の関数があって、コンパイラを喜ばせることになると思う。

NVIDIAのジョーダン・ハバードが開発したやつだね(FreeBSDも)。私の理解や経験から言うと、LLMのパフォーマンスは、その言語がトレーニングデータにどれだけよく表現されているかに依存する。だから、トレーニングコードが多くある既存の言語の方が、たとえその言語が複雑で理解しにくそうでも、実際にはLLMがうまくいく可能性があると思う。

新しい言語やフレームワークには、特に新しいアイデアがある場合、悲しいことにそういうことが多いと思う。

これって、あなたのワークフローによるところが大きいよね。優れた型付けや型チェック、良いコンパイラエラーを持つ言語は、表面のオーバーヘッドや構文が複雑な言語よりもループ内でうまく機能する。たとえそれがよく表現されていてもね。これは、例えば、https://github.com/toon-format/toon のようなJSONの代替フォーマットの背後にある考え方だよ。彼らはそのフォーマットでLLMの精度をJSONと比較してテストしていて、(一般的にJSONより少し進んでいる)。さらに、LLMに対して全体の言語を文脈に入れる能力、つまりすべてを説明する単一のドキュメントがあれば、ギャップを埋める可能性が高い。いくつかのナノファイルをざっと見たけど、見た目が好きとは言えないけど、すごくクリアに見えた。おそらくそれが利点になるね。

言語がどれだけよく表現されているかだけじゃないんだよね。あまり知られていないAPIはLLMを混乱させることがある。私はATProtoを使ったFlutterプロジェクトでAntigravityを使ってるけど、GeminiはDartコーディングが得意で、17番目の管理言語を習得するのが楽だった。FlutterのUI要素も得意だし。ただ、ATProtoとそのDart APIに関しては、明らかに苦手だった。失敗の特徴が面白かったよ。予想通り、過剰なリファクタリングは大失敗で、簡単に元に戻せた。でも、FlutterプロジェクトでAndroidのランチャーアイコンを再生成するようなシンプルなことは、完全に盲点だった。ジャングルを走り回る裸の野蛮人みたいにググらなきゃいけなかった。

正直、私の経験はそうじゃない。良いコードベースがあって、良いツールがあれば、そして本当に良いプロンプトがあれば、かなりマイナーなものでも素晴らしい結果が得られたよ。自家製の言語も含めてね。他の人が言ったように、鍵はフィードバックとプロンプティングだと思う。長いコンテキストを持つモデルなら、きっと理解できるはず。

その仮定は成り立たないと思う。例えば、最近になってエージェントがRustのコードを初回で正しく書けるようになったけど、過去にはそれが重要じゃなかったんだ。なぜなら、Rustのコンパイラやリンターがすごく良いフィードバックをくれるから、すぐにミスを修正できるからね。これによって文脈が少し早く埋まるけど、(1) 動的言語で問題をデバッグするほどではないし、(2) より良いエージェントフレームワークが出てきて、動的に文脈を圧縮するために「書き換え」てくれるようになるよ。

既存の言語に対して、こういう新しい言語への「トランスピレーション」レイヤーを作る方法があるのかな?そうすれば、他の言語からのトレーニングを全部活用できるかもしれない。ASTからASTへの何かね。ただ、最初のトレーニングやファインチューニングの段階でしか機能しないのかも。

私の理解/経験では、LLMのパフォーマンスは言語がトレーニングデータにどれだけよく表現されているかに比例する。 これは本当じゃないよ。LLMは文法をすごくよく理解してる。もしあなたの言語に文法があれば、LLMは一発で完璧なコードを生成できる。彼らが知らないのは、その言語の周りのツールなんだ。でも、これも結構簡単に解決できるよ。彼らはCLIツールを探るのが得意だから。

ブラックピルは、こういう理由で、今ある主流のプログラミング言語が、グローバルに関連する最後の(人間が設計した)言語になるってことだ。最終的にはAIが自分たちの言語を作るようになるだろうし、人間はもちろん趣味で言語をデザインし続けるだろう。でも、影響力の面では、プログラミングの世界を席巻するような人間の言語はもう出てこないと思う。残された時間があまりにも少ないからね。

大体同意するよ。良い表現と、すぐに自己修正できるツールの組み合わせが、新しい言語よりも短期的には良い結果を出すと思う。長期的には関係なくなると思うけど、すでにGPT3.5は「合成された」ゼロショットの文脈でプログラムの基本的な意味論を推論できてたからね。既存の言語の組み合わせとして説明するだけで(例えば、「RubyとINTERCALのCOME FROM」みたいに)や、文法を提供する(例えば、シンプルなEBNFと新しい/異なる構文に関するメモ)だけで、かなりうまく説明できてたし、見たことのないフランケン言語で書かれたプログラムが何をするかを推測できてた。新しい言語がその点で同じ土俵に立つための十分なトレーニングデータが得られる前に、モデルが十分に良くなることを期待すべきだと思う。だから、簡潔な言語仕様を提供するだけで済むようになるかもね。でも同時に、将来のモデルも既存の言語でうまく機能するようになるから、LLMに合わせて言語を調整するのは無意味になると思う。

クロードはElmが得意だね。トレーニングデータはかなり少ないはずなのに。

LLMに「教える」ために一つのMarkdownファイルを探してて、これを見つけた: https://github.com/jordanhubbard/nanolang/blob/main/MEMORY.m... 楽観的に、これをClaude Opus 4.5にシステムプロンプトとして全部突っ込んで、一発でプログラムを生成できるか試してみた: llm -m claude-opus-4.5 \ -s https://raw.githubusercontent.com/jordanhubbard/nanolang/refs/heads/main/MEMORY.md \ 'この言語でマンデルブロ集合のCLIツールを作って' > /tmp/fractal.nano そのトランスクリプトはこれ。コードは動かなかった: https://gist.github.com/simonw/7847f022566d11629ec2139f1d109... それで、nanolangのチェックアウト内でClaude Codeを立ち上げて、コンパイラの実行方法を教えて、問題を修正させたら…それはうまくいった。こちらがそのトランスクリプト: https://gisthost.github.io/?9696da6882cb6596be6a9d5196e8a7a5... 完成したコードとその出力はコメントに: https://gist.github.com/simonw/e7f3577adcfd392ab7fa23b1295d0... だから、良いLLMがあれば、既存のドキュメントにアクセスできて、そのコンパイラを実行できる環境があれば、この言語を使いこなすのは間違いないね。

./docsのすべてを与えるか、エージェントにそのファイルにアクセスさせて参照できるようにする必要があると思う。あなたが投稿したMEMORY.mdファイルには、./docs/CANONICAL_STYLE.mdや./docs/LLM_CORE_SUBSET.mdが言及されていて、それらは間接的に他の機能やドキュメントフォルダ内のファイルについても言及している。

でも、そうすることで特定のタスクに対するLLMの問題解決能力が落ちちゃうんじゃない?

おお、すごい。READMEの制御フローが、より大きい/小さいを示すためのプレフィックス表記がちょっと面倒だなと思った。# 制御フロー if (> x 0) { (println "positive") } else { (println "negative or zero") } でも、マンデルブロの例でのcase/switch文を求める叫びには敵わないね… # グラデーション: " .:-=+#%@" let gradient: string = " .:-=+#%@" let gradient_len: int = 10 let idx: int = (/ (* iter gradient_len) max_iter) if (>= idx gradient_len) { return "@" } else { if (== idx 0) { return " " } else { if (== idx 1) { return "." } else { if (== idx 2) { return ":" } else { if (== idx 3) { return "-" } else { if (== idx 4) { return "=" } else { if (== idx 5) { return "+" } else { if (== idx 6) { return "*" } else { if (== idx 7) { return "#" } else { if (== idx 8) { return "%" } else { return "@" }

効果とトークン効率の両方を考慮する必要があるね。全てのトレーニングデータを持つ言語と比べて、分布外の言語が同じくらい効果的だとは思えない。文脈を理解するために、エージェント向けの「始め方ガイド」が必要だし、PythonやRustなどで同じタスクをやった場合との評価も必要だね。

本当にエージェント指向の「始め方」ガイドが必要だね。文脈を理解するために、PythonやRustなどで同じタスクを行った場合との評価も必要だし。いくつかのそういうドキュメントがあって、約1400行のMEMORY.mdファイルが他のいくつかのファイルを参照しているし、言語仕様書や、ジョーダンがこの言語やその実装の進化について考えたことがほぼすべて詰まった約100のドキュメントのコレクションもある。そして、SDL2を使ったOpenGLプログラムを含む例のコレクションもある。明らかに、jkhは約5ヶ月前に自分でホストしたプログラミング言語でLLMをブートストラップする必要性を理解しているね。

今のところ、新しいプログラミング言語は必要ない気がしてきた。むしろ、仕様を作成する新しい方法が必要だと思う。LLMが擬似コードからコードへの翻訳者として機能できるという仮説があるんだ。擬似コードはコードっぽい仕様と自然言語の混合を許容できるからね。これによって、人間が仕様を決める役割を明確にし(これは必ずやらなきゃいけないことだし)、LLMがコードを書く役割を担うことになる。これにより、リソースが少ない「非フロンティア」モデルももっと役立つかもしれない。さらに、文法ミスや最悪の場合には自然言語にも耐性があるからね。つまり、LLMには新しい言語は必要ないと思う。必要なのは私たちだ。

そして、全てが一周するねXD。

仕様を作成する新しい方法。それって結局プログラミング言語のことだよね。今のLLMの本当の問題は、コードを素早く生成できるかどうかは関係ないってことだ。誰かがそれを読んで、確認して、テストしなきゃいけないからね。もしかしたら、簡潔なプログラミング言語が必要かも。すぐに読めて、確認できるようなもの。これを仕様と呼ぶことができるかも。

これがAgintのアプローチだね。まずコードの構造を上から下にグラフとして推論して、その後に型を追加して、型を入出力の関数シグネチャとして解釈して、最後にコード生成のために関数を「インペイント」するんだ。

似たようなことを考えてたんだ。先週末にちょっとした実験をして、LLMを使って擬似コードの構文を強調するものを作ったよ: https://x.com/danielvaughn/status/2011280491287364067?s=46

これ、2つの異なることを混同してると思う。 - LLMは擬似コードからコードへの翻訳者として機能できる(これは得意だよ) - LLMはバグを作ったりエラーを起こしたりするし、その割合は基盤となる言語の「複雑さ」や「バグの多さ」に直接比例するというのが妥当な仮説だと思う。つまり、AIに足元をすくうツールを与えれば、無自覚にそれを使っちゃうってこと。でも、だからといって擬似コードをすぐにコードに変換できないわけじゃない。これらは、あなたの論理が目標に対して大きく間違っている場合、LLMが常に擬似コードを魔法のように修正できるわけではないけど、バグを減らす新しい言語から大きな恩恵を受けると思う。今がそういう言語を作るチャンスだよ。LLMは人間にはできないことを最適化できるから、LLMに合ったバグを減らす新しい言語を設計することが可能だと思うけど、人間にはあまり効果的じゃない(文法、使いやすさ、冗長性、その他いろいろのせいで)。これはめちゃくちゃ重要だよ。なんでかっていうと、今後20年間に書かれるコードの99%はAIによって書かれるから。そして、これまでに書かれたコードの100倍の量になるだろう(やるコストがほぼゼロになったから)。つまり、言語技術に革命が起こらない限り、期待できるバグや脆弱性の数も100倍になるってこと。だから、こういうアイデアが必要なんだ。私もこれを信じていて、LLMを特にターゲットにした何かを作ってるし、去年の11月中旬から後半にかけて取り組んでるよ。ビジネスモデルがあれば、その言語を持続可能にできる。

我々が必要なのは、既存のコードベースに適用される差分を、コードベース自体と同じくらい明確に定義するプログラミング言語だよ。つまり、イベントソーシングが一連の変更イベントから状態を具現化するのと同じように、この言語は「修正指示」の一連からコードベースを具現化する必要があるんだ。異なるモデルは、同じ指示の系列を使って異なるコードベースを具現化するかもしれない(コンパイラみたいに)、あるいは異なる「環境要因」(例えば、利用可能なデータベースやクラウドプロバイダー)を考慮するかもしれない。まるでコードベース自体が重要なアーティファクトではなく、プロンプトのシーケンスが重要になっているみたいだね。このプロンプトのシーケンスを使って、コードベースとは完全に独立したテストスイートを生成することもできるよ。

実はこれを作ってるんだ。来月の初めにリリース予定だよ。プロフィールに見るためのURLを追加したから、今週中には見れるようになるはず。オープンソースになるよ。

ああ、みんなが光を見始めてるね。これは航空業界のような一部の産業から抽出できることなんだ。ソフトウェアの仕様(要件、アーキテクチャ文書など)がソフトウェア自体よりも重要な場合があるからね。問題は、自然言語自体が曖昧で、明確な仕様の重要性を人々が本当に理解していないことだ(要件で指定された制限には単位や許容範囲を入れるよう何回も繰り返してきた)。もう一つの問題は、自然言語には「デフォルト」がないことだ。何かを指定しないと解釈の余地があるからね。そして、人々は「うん、これはわからない」と言う代わりに何かを解釈するんだ。

d2langやsequencediagram.org、bpmn.ioのXML(OMG XML)みたいなアーキテクチャ用のマークアップがいくつかあるんだけど、質問はこれらをマスターできるかってこと。しばらく新しいものを発明せずに済むかな?追記:上記の組み合わせは、私のエージェント的なコーディング冒険でかなり良い結果を出してるよ。

もうPRは、変更や機能を完全に仕様化したLLMプロンプトから始めるべきだと思ってる。そしたら、複数のLLM生成の実装を見て、プロンプトをどう更新すべきか考えることができる。これで、コードの背後に完璧な意図があって、もし間違ってたらその意図を洗練できるんだ。

著者のGitHubプロフィールからリンクされてるウィキペディアのページがあるよ: https://en.wikipedia.org/wiki/Jordan_Hubbard 要約: - FreeBSDを共同開発した。 - AppleでUNIX技術を13年間リードした。 - iXSystemsでFreeNASのリーダー。 - Uberについて何かあるらしい。 - NVIDIAでGPUコンピュートソフトウェアのシニアディレクター。 まあ、どうでもいいけど。

別名jkh。懐かしいね。初期のFreeBSDの頃、Jordanはメーリングリストのトラフィックをさばいて、みんなが質問を投げかけてくる中でプロジェクトをまとめてたんだ。彼がいつ寝てたのか不思議だったよ。彼もそう思ってたみたいだね[1]: 「2.0ポートコレクションの始まり。まだsupリポジトリはないけど、また起きたら作るよ.. :)」 提出者: jkh 1994年8月21日 [1] https://github.com/freebsd/freebsd-ports/commit/7ca702f09f29... Ports 2.0の興味深いコミット。bashが3バージョン、Emacsが4バージョン、さらにjoveも。

関数ごとのテスト必須ってちょっと面白いね。でも、テストが何か有用なことをすることが強制されてるわけじゃないよね?だから、すべての関数に対して100%のパスカバレッジでテストされることが求められる言語で書くのがどれだけ疲れるか気になるな。もちろん、これだけではコードを証明することにはならないけど、コードのコーナーケースにすぐに目を向けるきっかけにはなるだろうし、静的な関係のために100%のパスカバレッジでテストできないコードは書けなくなるだろうね。例えば、if (foo) { if (!foo) {..} }みたいな感じで。そんな言語には、テストをモックするための動的依存性注入メカニズムが必要になるのかな?

Forthを使ってLispを作り、LispでTclを実装して、Tclを使ってSmalltalkを作り、最後にSmalltalkでForthを構築する。でも待って、まだ始まったばかりだよ!

面白いね。構文がCとSchemeの不義の子供みたいだ。(誤解しないでほしいけど、プレフィックス表記の明確さは好きだよ。)

コンテキスト:このプロジェクトはFreeBSDの共同創設者で、元Appleのエンジニアリングディレクター、ジョーダン・ハバードによるもの。彼は今、Nvidiaのシニアディレクターだって、彼の公開LinkedInによるとね。