1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Python風に書いてRustで動く言語「Nagi」を作る理由【v0.1.11・Windows実行例】

1
Posted at

この記事は 2026年10月9日時点 の情報で書いています。
対象は、2026年10月7日に公開したコンパイラ Nagi 0.1.11 です。
main ブランチには未リリースの実装もあります。
未リリースの機能は、その都度区別して紹介します。

はじめに

PythonHaruです。バックエンド向けのプログラミング言語「Nagi(凪)」を個人開発し、GitHubで公開しています。

今回は最新機能を振り返りつつ、Nagiを作っている理由と、Windowsで動かす手順をまとめました。

  • Nagiで解決したい課題
  • 現在使える機能
  • Windowsで実際に動かす手順
  • 現在の制限と今後の予定

Nagiは開発中のため、仕様やAPIが変更される可能性があります。記事内では公開済みの内容と開発中の内容を分けています。

Nagiとは

Nagiは、Pythonに近い構文で型付きのプログラムを書き、Rustのライブラリや自作コードを組み合わせて動かせる、バックエンド向けの言語です。

現在のコンパイル処理は、次の流れになっています。

Nagi High (.nagi)
       ↓
Nagi Low (.low)
       ↓
Rustコード生成
       ↓
Cargo / rustc
       ↓
ネイティブ実行ファイル

普段書くのは、インデントで構造を表すHighです。一方のLowは波括弧を使う構文で、生成結果を読み解いたり、関数を差し替えたりするときに使います。

Lowでも型や所有権の規則はHighと共通です。生ポインターやunsafe、メモリ配置の指定、C ABIは現時点で扱えません。

名前の「凪」には、内部で複雑な処理が動いていても、コードを書く側からは落ち着いて見通せるようにしたい、という思いを込めました。所有権や失敗の扱いまで隠すのではなく、必要なことを自然に読み取れる言語が理想です。

参考: README / 公式サイト

なぜ新しい言語を作るのか

Rustには性能や安全性、充実したライブラリがあります。一方、バックエンドを作る際にはHTTP、認証・認可、バリデーション、DBなどを組み合わせる必要があります。

Nagiでは、そうした処理同士の境界を、できるだけ簡潔に書けることを重視しています。

想定しているのは、たとえば次のような人です。

  • Rustを知らなくても、APIを作りたい人
  • Rustのライブラリや独自の基盤を、組み合わせて使いたい人

よくあるAPIならNagiの標準機能で完結させ、特殊な処理が必要になったらRustの既存資産を利用する。この使い分けをしやすくしたいと考えています。

そのため、現時点では次のような方向を取っています。

  • RustやGoの置き換えは目標にしない
  • HTTPやDBの基盤を、独自実装すること自体は目的にしない
  • Pythonと同じ動作をする言語にはしない

Pythonに似ているのは主に見た目です。実際に書くときには、Nagiの型やmove、view、Resultを使います。

参考: DESIGN.md

Nagiが目指すもの

設計を考えるとき、特に大切にしていることがあります。

「安全性を、開発者の注意力だけに依存させない」

バックエンドでは、メモリ管理だけでなく、次のようなところでも実装ミスが起こります。

  • 認証・認可
  • SQL
  • 並行処理
  • リソースの寿命(接続を閉じ忘れる、など)

レビューや開発者自身の注意はもちろん大切です。ただ、よくあるミスを型検査や標準API、ランタイム側でも防げれば、もっと安心して開発できるはずです。

コンパイル時の検査には限界があります。たとえば認可のコールバックに書かれた業務上の判断が正しいかどうかまでは、Nagiが静的に証明できません。

現在どこまで進んでいるのかも整理してみました。

目標 状況
型・所有権(move / view / shared)による値の扱い 公開版0.1.11で実装済み
業務上の失敗と基盤の障害を分ける(Result / Task) 公開版0.1.11で実装済み
HTTP・JSON・SQLiteを標準APIで扱う 実装済み(制限あり。後述)
SQLの事前検査 実装済み(範囲は限定的)
認証・認可の境界を型で表す 実験的機能のみ。強化は開発段階
SQLite Pool / Transaction main にはあるが、0.1.11には未収録
認証・認可のpolicy必須化(AuthScopeなど) main の開発source。設計・実装途中で未リリース
メモリ使用量や処理コストを把握しやすくする 方針。すべてのコピーが明示される状態ではない
小規模APIから大規模サービスまで 長期的な目標

表のとおり、公開版で使える機能もあれば、まだ設計・実装を進めている機能もあります。

Python・Go・Rustなどから取り入れたい考え方

言語設計では、既存言語の使いやすいところも参考にしています。

言語 参考にしたい強み
Python 読みやすい構文、簡潔な記述
Go シンプルな開発体験、並行処理、運用のしやすさ
Rust 所有権、借用、安全性、ネイティブ性能
Zig 明示的なメモリ管理、コストの可視化

それぞれの仕組みを参考にしつつ、バックエンドで扱いやすい形に整理していきます。

Actorとその監視については、Elixirの考え方も参考にしています。もちろん、各言語と同じ挙動を再現するという意味ではありません。

また、Zigのようなコストの見えやすさについては、まだ改善の余地があります。たとえばHTTP応答のtextは、借りた文字列から応答用の所有値を作ります。内部のコピーやメモリ確保が、すべてソースコードから分かる状態にはなっていません。

Nagiのコンパイラ構造

コンパイラとRust側の役割も分けています。

Nagi側がやること

  • 構文解析、名前解決
  • 型・move・viewの検査
  • Rustコードの生成

Rust / Cargoに任せること

  • 依存crateのビルド
  • 最終的な借用・traitの検査
  • 最適化、機械語の生成

現在はRustを経由してネイティブ実行ファイルを生成しており、独自の機械語バックエンドはありません。

Tokio、Hyper、Serde、rusqliteなどを利用できるので、Nagi独自の構文やAPIの開発に集中できます。一方、Rustの開発環境が必要になり、Cargoのビルド時間や生成コードとの整合性も考えなければなりません。

checkが通ってもbuildが落ちることがある

nagic checkではNagi側の構文や型などを検査します。

checkに成功しても、Rust側の型・借用検査によってbuildが失敗する場合があります。また、Nagi側の検査で、Rustなら通るコードを拒否することもあります。

Nagi側で検出すべき問題がRustのビルド時に初めて見つかった場合は、コンパイラの改善対象として扱います。特に複雑なviewの再代入やRustのtrait境界には、まだ検査の差が残っています。

Highは一度Lowに変換される

たとえばhello.nagiに対してnagic lower hello.nagiを実行すると、HighからLowへの変換結果を確認できます。

hello.nagi
def main():
    print("こんにちは、Nagi!")
    count = 3
    print(count * 2)

生成されるbuild/hello/generated.lowの本体部分は、次のようになります。

fn main() -> unit {
    print("こんにちは、Nagi!");
    let count: i64 = 3;
    print((count * 2));
}

count = 3の型がi64と判断されていることも分かります。
Highの処理は、検査後にこのLowのテキストを経由して、再解析・再検査されてからRustに変換されます。

Lowに書き換えても、それだけで処理が速くなったり、Rustの借用検査を回避できたりするわけではありません。今は、生成内容の確認や、Highを変更せずに関数を差し替えられることに価値があると考えています。

0.1.11までの進化

0.1系で追加してきた機能を簡単に振り返ります(詳細はCHANGELOG)。

  • 0.1.7〜0.1.8: 標準HTTPサーバー(std.http.server)、actor(std.actor)、コード構造の可視化(nagic map)
  • 0.1.9〜0.1.10: Result変換、Supervisor、SQLの事前検査、アンインストーラー
  • 0.1.11(2026-10-07): Task結果ハンドル、明示的なmove、ビルド処理の改善など

0.1.11での主な変更をもう少し紹介します。

1. Task結果ハンドル

task = spawn work() で作ったタスクの結果を、await task で一度だけ受け取る仕組みです。
すべての結果は、awaitするか、明示的に捨てる(discard)必要があります。
タスクの結果を扱い忘れないよう、コンパイラが確認する仕組みです。

scopeは、子タスクが終わるのを待ってから出口に進みます。
業務上のErrと、タスク自体の故障(TaskFailure)も分けて扱います。

discardは結果を受け取らないことを明示する操作です。タスクを停止したり、終了を確認したりする操作ではありません。

2. 明示的な std.ownership.move

非Copyのローカル変数をそのまま a = b で代入すると、check で拒否されるようになりました。
値と後片付けの責任を渡したいときは、a = move(b) と書きます。
所有権が移ることを、コードから分かるようにしています。

3. ビルド世代の分離

同じアプリを再ビルドしても、以前に成功した実行ファイルは壊れません。
同じ生成先への書き込みはOSのロックで直列化され、成功したときだけ「最新」が更新されます。
ビルド後は、表示される native: のパスの実行ファイルを使います。

4. 検査済みの情報を封印してからRustを生成

CheckedProgram という不変の形にしてから、Rustコードを生成するようにしました。
コンパイラ内部の変更ですが、検査した内容と生成されるコードの食い違いを減らすための土台になります。

5. 認証・認可の実験的な型

std.auth.Principal と、名前で区別される Grant[P] を追加しました。
まだ実験段階の機能で、Nagiのコードから直接生成したり、コピー・共有したりすることはできません。
同梱のサンプルは固定の認証情報を使うもので、JWS検証や、一般的な認可の解析はしていません。

Windowsで実際に動かす

ここからはWindows 11でNagiを動かしてみます。手順は導入ガイドとREADMEに沿っています。

事前準備

アプリをビルドするには、次の環境を用意します。

  1. Rust / Cargo(MSVC toolchain): 公式のインストール手順
  2. Visual Studio Build ToolsのC++開発環境
  3. Nagiコンパイラ(次の手順)

nagic --version や nagic --help だけなら、RustもBuild Toolsも不要です。
nagic run や nagic build を使うときに必要になります。

インストール

PowerShellから公式インストーラーを実行します。管理者権限は必要ありません。

& ([scriptblock]::Create((Invoke-RestMethod 'https://raw.githubusercontent.com/disnana/Nagi/main/scripts/install.ps1')))

このコマンドは取得したスクリプトを実行します。実行前にinstall.ps1の内容と配布元を確認してください。インストーラーは公開Releaseからファイルを取得し、SHA-256を照合する仕組みです。

インストール後はバージョンを確認します。

nagic --version

0.1.11を導入した場合はnagic 0.1.11と表示されます。コマンドが見つからないときは、ターミナルを開き直してみてください。

Hello World

好きなフォルダーにhello.nagiを作ります。インデントは空白4つにしてください。

hello.nagi
def main():
    print("こんにちは、Nagi!")
    count = 3
    print(count * 2)

実行します。

nagic run hello.nagi

実行結果は次のとおりです。

こんにちは、Nagi!
6

初回はCargoによる依存ライブラリのビルドがあるため、少し時間がかかる場合があります。

検査とビルドを別々に行うこともできます。

コマンド 何をするか
nagic check hello.nagi Nagiの構文・型・所有権を検査
nagic lower hello.nagi 検査して、Lowを出力
nagic build hello.nagi Rust側の検査を含めて、実行ファイルを生成
nagic run hello.nagi ビルドして実行

HTTPサーバー

server.nagi を作ります。

server.nagi
import std.http.server as http

class State:
    greeting: str

async def hello(request: http.Request, state: shared[State]) -> Result[http.Response, Error]:
    return ok(http.text(http.Status.OK, view(state.greeting)))

async def main() -> Result[unit, Error]:
    app = http.app_default[State](State(greeting="Hello, Nagi!"))
    app = try http.route(app, http.Method.GET, "/", hello)
    return await http.serve(app, 8080, http.default_options())

コードの中で使っている主な機能は次のとおりです。

  • shared[State]: 共有された状態を、ハンドラーが受け取る
  • view(state.greeting): 文字列を、コピーせず借りて渡す
  • Result[..., Error] と try: 失敗を例外ではなく、戻り値で扱う(try はErrの伝播で、Pythonの try/except とは別物)

実行します。

nagic run server.nagi

サーバーを起動したまま、別のPowerShellでアクセスします。

Invoke-WebRequest http://127.0.0.1:8080/ |
    Select-Object -ExpandProperty Content

期待される応答です。

Hello, Nagi!

サーバーを止めるときは、起動したターミナルで Ctrl+C を押します。

現在の標準HTTPサーバーは、loopback(同じPC内)でのHTTP/1に対応しています。TLS、WebSocket、streaming向けの公開APIはまだ用意されていません。

サンプルは公式READMEを元にしています。Linux版0.1.11で両サンプルのnagic checkが通ることを確認しています。Windowsでの実行結果は公式Docsに記載された期待値であり、この記事用にWindows実機で再検証したものではありません。

VS Code・JetBrains連携

VS Code

Marketplaceから入れられます(拡張機能の名前は Nagi Language、ID は Disnana.nagi-lang)。
記事作成時点で確認した公開版は0.1.13です。

  • 保存時の型検査(エラーはProblemsに表示)
  • 補完、ホバーでの型表示
  • F12での定義ジャンプ
  • 実行・ビルドのコマンド

JetBrains(IntelliJ IDEA / PyCharm)

JetBrains Marketplaceに掲載しています。
GitHub Releasesには、両IDE共通のZIP(0.1.2)が案内されています。導入前にReleasesで公開状況と対応バージョンを確認してください。

現行のJetBrains版では、次の機能を利用できます。

  • High / Lowの構文ハイライト、インデント、折りたたみ
  • check / runコマンド、行番号横の実行ボタン

コンパイラと連携した補完、定義ジャンプ、編集中の診断は今後の実装対象です。VS Code版とは対応機能が異なります。

コンパイラとエディター拡張は別々にバージョン管理しています。また、どちらのエディター拡張にもコンパイラは含まれないため、Nagi本体を別途インストールしてください。

現時点の課題と制限

使う前に知っておいてほしい制約もまとめておきます。

言語とコンパイラ

  • 言語仕様とAPIは開発中で、変わります
  • check が通っても、Rust側で build が失敗することがあります
  • ユーザー定義のgeneric・traitは未対応です
  • Mapの標準操作は未対応です

HTTP・DB

  • 標準HTTPサーバーはloopbackのHTTP/1のみです
  • SQLの事前検査は、スキーマを明示して使います
    • 確認できるのは、クエリ名・返却列・bindの数まで
    • 値の型、NULL可否、動的SQLは実行時の検査です
  • 標準のPostgreSQL、DB間共通のAPI、pool・transactionは、公開版0.1.11にはありません
    • SQLiteのPool / Transactionは main に入っていて、未リリースです

Rust連携

  • 任意のRust型を、そのままNagiから使うことはできません
  • 安定した外部ABIもありません
  • 普通のアプリでも、Rustに降りる場面が残っています

認証・認可

  • 0.1.11にあるのは実験的な型だけです
  • main で進めているpolicy必須化も、未リリースです

実績

  • RustやGoより高性能だと示す比較結果は、現時点ではありません
  • 性能については測定条件と生ログを公開しています。今回の記事では言語間の数値比較は扱いません

今後の開発方針

ロードマップで、優先している課題は次のとおりです。

  1. 言語規則を揃える(owned・view・move、分岐やloopの検査)。check 後のRustビルド失敗を再現例で追って、どちらで診断すべきかを区別する
  2. Rust資源との境界を決める(不透明な型、借用、共有、取消後の処理)
  3. 標準ライブラリを、moduleとして整理する
  4. DBを汎用化する(SQLiteとPostgreSQLは別module・別資源型にする方針)
  5. HTTP基盤の比較(Axum / Towerを第一候補に、同じ条件で評価してから決める。採用は未決)
  6. 実例と文書で、境界を確かめる

また、0.2.0に向けてSecurity Foundationの設計も進めています。認証・認可、CSRF、HTML出力、SQL、送信先の検証、CORS、Cookie / Sessionなどを対象に、各機能の責務を整理しながら実装する計画です。

main では、その最初の一歩(SF01)として、標準HTTPの全routeにpolicyを必須にする実装を進めています。
この変更はまだ公開版に入っていません。0.1.11では利用できず、統合検証を経て0.2.0として公開する予定です。

独自バックエンドやself-hosting、独自VM、分散Actorについては、現段階で実装に着手しておらず、採用するかどうかも未定です。

おわりに

ここまで紹介したとおり、NagiではHTTP、SQLite、Task管理などを実装しながら、型や所有権、認証・認可の扱いも少しずつ整理しています。

バックエンドでは、処理自体は単純でも、認証やDB、並行処理といった境界で考えることが増えます。そうした部分を言語や標準APIで扱いやすくし、実装ミスも減らしていきたいです。

安全なバックエンドを、もっと自然に書けるようにする。

Nagiはまだ開発途中ですが、この方向性で改善を続けています。Windows向けのサンプルも用意しているので、興味があればぜひ触ってみてください。使いにくかったところや不具合、設計への意見も歓迎します。

開発の進捗はXでも発信しています。記事が参考になれば、いいねやストックをしていただけるとうれしいです。

参考リンク

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?