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

なぜジャネットなのか? (2023)

概要

  • Janetはシンプルで強力なLisp系プログラミング言語
  • ネイティブバイナリへの配布や埋め込みが容易
  • テキストパースやサブプロセスDSLが優秀
  • ミュータブル・イミュータブルコレクションやマクロも充実
  • 独自の快適な文法と思想

Janetを使うべき理由

  • Janet は、現代的で親しみやすい Lisp方言 のプログラミング言語
  • 私はJanetに魅了され、本まで執筆し無料公開
  • Janetの魅力を知ってほしいと考え、ここでその理由を紹介

Janetのシンプルさ

  • 命令数 はわずか8つ(do, def, var, set, if, while, break, fn)
  • JavaScriptに似た ランタイムセマンティクス で直感的
  • 標準ライブラリも 1ページに収まる ほどコンパクト
  • 学習コスト が非常に低く、午後だけで習得可能

Janetの配布・実行形式

  • Janetプログラムは ネイティブバイナリ に簡単に変換可能
  • Janetランタイムごと 静的リンク され、配布先に依存不要
  • Cコンパイラ でビルドする仕組みにより、自己埋め込み型バイナリ生成
  • 1MB未満の 軽量バイナリ (例: 784K on aarch64 macOS)
  • Janetランタイム・GC・バイトコードコンパイラすべて内包

Janetのテキストパース能力

  • 正規表現ではなく Parsing Expression Grammar (PEG) ベース
  • マルチラインやHTML/JSON/バイナリフォーマットも 柔軟に解析
  • 構造的かつ合成可能なパーサ が標準で利用可能
  • 学習も 簡単

JanetのサブプロセスDSL

  • サードパーティライブラリ sh による強力なシェルスクリプトDSL
    • 例: ($ find . -name *.janet | say)
  • パイプやリダイレクト を直感的に記述可能
  • PerlやBashの代替としても 優秀

Janetの埋め込み性

  • Janetランタイムは 小さなCライブラリ
  • Cから簡単に 関数呼び出し・値操作 が可能
  • ウェブサイトへの埋め込みや カスタムDSL 構築にも最適

Janetのコレクション型

  • ミュータブルイミュータブル 両方のコレクションを標準搭載
  • イミュータブルコレクションは 値セマンティクス
  • ミュータブルコレクションは 参照セマンティクス
  • 標準ライブラリに イミュータブル複合値 があるのは珍しい特徴

Janetのマクロ

  • マクロを学ばずとも 通常利用可能
  • コード生成 の楽しさと強力さ
  • Janetのマクロは 衛生的ではない が、 リテラル関数のアンコート で透明性確保
  • 抽象構文木操作 がシンプルかつエレガント

Janetのコンパイル時・実行時の値受け渡し

  • 値のシリアライズスナップショット実行 が可能
  • コンパイル時にトップレベルのコードを 実行し状態を保存
  • 共有参照やジェネレータの状態も保持
  • マクロ無しでもこの仕組みを活用可能
  • 例: ゲームのスプライン事前計算やアセット埋め込み

Janetの文法と書き心地

  • シンプル・統一感・多様性の 絶妙なバランス
  • 括弧を多用しつつ、[]や{}で 構造を明確化
  • ミュータブルリテラルは @記法
  • 無名関数や 関数リフティングの簡潔記法
  • バッククォートで エスケープ不要な文字列リテラル
  • 可変長引数 は&記法
  • リーダーマクロ非対応 で文法の一貫性維持

Janetの思想と伝統との違い

  • CAR→first, PROGN→do, LAMBDA→fn, SETQ→def
  • nilは独自型で 空リストではない
  • 真偽値はファーストクラス
  • EQ, EQL, EQUAL, EQUALPなどの 伝統的関数は非搭載
  • 連結リスト不在 で現代的な設計
  • 伝統的Lispのイメージに囚われず、 新しい快適さ を追求

Hackerたちの意見

この投稿は新鮮だね。まるでAIが登場する前のネットの議論を思い出すよ。新しい言語、新しい構文、何年もコードを書いてきた人たちとの激しい議論。AIを禁止したオンラインコミュニティを誰か作るべきだと思う。

数ヶ月前になるけど、HTTP経由でLLMとコミュニケーションするためにジャネット言語でツールを作ったんだ。もちろん、たぶんクロードコードに書いてもらったんだけどね。思ったよりも良い結果になったよ。実行ファイルのサイズが小さくて本当に驚いた。あの時までNode.jsでしかウェブ開発をやったことなかったから。

誰かがAIを禁止したオンラインコミュニティを作るべきだと思う。もしこのサガを追っていなかったら、最新のdigg.comのリローンチはボットの襲撃に対処できずに失敗したんだ。最初にAIをオンラインコミュニティから排除する信頼できる方法を見つけた人は、きっととても裕福になるだろうね。実際、今は新しいホームページがあるみたいだから、実質的には第二の最後だね。

新しい構文ってどういうこと?構文が新しいって言うけど、与えられた例の外側の括弧を見ればリスプっぽいじゃん。

AIのすごいところは、AIに超ファンがいなくても、AIに触れない会話に無理やり入れてくることだよ。反対派が勝手にやってくれるからね。

AIが許可されていないオンラインコミュニティ。これについてはよく考えるんだけど、匿名性を壊さずにどうやって実現するかが難しい問題だよね。「人間である証明」みたいなものを持つのは、本当に難しい。

でも、リテラル関数の引用を外せることで、ジャネットは完全に参照透過なマクロを書くことを可能にしてるんだ。このリスプの人たちは、すごく抽象的なことに興奮するよね。これを通りすがりの普通の人に言ったら、たぶん逃げ出そうとするだろうね。

オブジェクト指向プログラミング言語とその利点を「通りすがりの普通の人」に説明したことある?

平均的なプログラマーも同じだよ。正直言うと、リスピーコミュニティは小さいおかげで恩恵を受けてると思う。例えば、今や古典的なデザインパターンが「継承よりもコンポジションを好むべき」って警告してるのに、オブジェクト指向プログラマーたちは15階層も深い階層を作っちゃったりしてるんだよね。

非常に抽象的なこと A Cマクロでリテラルがあって参照透明性がない: #define MULTIPLY(x, y) x * y int result = MULTIPLY(2 + 3, 4); // 14 何かの意味がわからないからって、それが悪いわけじゃないよね。君が言いたかったのはそういうことだと思うけど。プログラミングで起こるパターンや問題の共通言語を持つのは良いことだよ。 「あのプログラマーたちって変だよね」っていう理由でそういう用語をバカにするのは無意味で逆効果だと思う。

このリスプの人たちは、本当に抽象的なことに興奮するよね。もしこれを道端の普通の人に言ったら、たぶん逃げようとするだろうな。参照透過性って、プログラムが何をしているかを理解するのに役立つすごく強力な機能なのに、名前が面白いだけで、そんなに抽象的なことじゃないんだ。名前にビビらないで。なんでこんな変な名前なの?って聞いてもいいけど、専門家は「普通のこと」に対しても専門用語を使うから、そういうもんだよ。車の整備士も、車にとって絶対必要な部品に変な名前を付けるけど、説明してもらえばそんなに難しくないしね。

ジャネットが注目されるのを見るのはいつも嬉しい。現代的な機能の一つに感謝を込めて: サンドボックス「特定のシステムリソースを使用しないように機能セットを無効にします。一度機能が無効になると、再度有効にする方法はありません。」

すごくクールな機能だけど、普通のプログラマーがそんなサンドボックスを必要とするシナリオって何だろう?

それから、同じ開発者が作った前の言語であるフェンネルも似たようなもので、Luaにコンパイルされて、完全にLuaで実装されてるんだ。独自の標準ライブラリはないから、ジャネットのパーサーライブラリみたいな便利なものが欠けてるけど、Luaを埋め込んだもののスクリプトを書くにはいいよ。 https://fennel-lang.org/

Fennelは本当に素晴らしいし、Clojureファミリーに入るのにいい方法だよ。唯一の不満は、デバッグが典型的なトランスパイルの針山になってるところかな。FennelとLua VMの橋はすごく脆弱で、JanetのデバッガーやREPLの半分のクオリティもないんだ。これは本当に残念で、Fennelはもっとポータブルだし、LuaJITのおかげでSBCLをぶっ壊すこともできるのに、マジでクレイジーだよ。でも、トランスパイルの体験が完全に足を引っ張ってると思う。ワークアラウンドもできるけど、debug.setinfoを実装しても、match-blocksみたいな楽しくないエッジケースにぶつかることになる。LuaJIT2をフォークして、デバッグやエラーの構造を再構築すれば、言語の透明性にもっと適したものになると思う。そうすれば、Fennelのような言語がもっと魅力的になるだろうね。

作者はこれをジャネットを使って作ったんだ(過去にHNで話題になったよ): https://bauble.studio https://toodle.studio この二つの魅力的なアートツールは、しばらく前にジャネットにすごくワクワクさせてくれたんだ。

これがジャネットに初めて出会ったときに本当に役立ったドキュメントだよ: https://janetdocs.org/tutorials https://janet.guide/ (作者のやつ)

SETQはdefだよ 最初は「何?」って声に出しちゃった。SETQはバインディングを作らないから、ただ更新するだけだし、ドキュメントを読んでみたら(https://janet-lang.org/docs/bindings.html)作者は確かに間違ってる。「defで作られたバインディングは不変」って言ってるから、彼は「SETQはセットだ」って言いたかったんだと思う。ジャネットが好きになりたいんだけど、Guile、Tcl、CLの間のちょうどいいところにあるみたいで(SBCLのスピードや成熟度は別として)、ラムダや制御フロー演算子に使われる角括弧(ベクター)にはどうしても抵抗があるんだ。Clojureと同じで、どうしてもそれを乗り越えられない。努力すれば克服できるかな?それと、今のLSP/SLIMEの状況はどうなってるの?最近はすごく重要だよね。

Clojureの構文における角括弧の使い方は、すごく一貫していて論理的だよね。丸括弧が使われると、リストの最初の要素が残りのリストの解釈を決めるんだ。例えば、(func a b c) は関数をそのパラメータで実行するし、(macro x y z) はマクロをそのパラメータで展開する。([p q r] …) は、パラメータのベクターから始まる「裸」の関数本体で、その後に実行可能な形式が続く。角括弧は同じ「種類」の要素が使われるところで使われて、最初の要素は特別じゃないんだ。例えば、(defn f [a b c] …) は同じ種類のパラメータのコレクションで、最初のパラメータは特別じゃないし、(let [a 1 b 2] …) もバインディングのコレクションで、最初のバインディングは特別じゃない。思いつく唯一の例外は、case で複数の一致する要素をグループ化することだけど、これは使いやすさのためだね。論理が分かって、どれをいつ使うかを理解したら、考えが変わって、それ以来美しいと感じてる。

著者は、Janetが 組み込み ネットワーキングを持っていることに触れていないね。これまでにいろんな小さなインタープリターを試してきたけど、これは結構珍しいと思う。

シェルDSLがあったからこそ、Janetを試してみたいと思ったんだ。

Janetにはちょっと不満がある。主に、パッケージ管理のバージョニングがないことと、ライブラリが全体的に不足してること(高度なHTTPルーティングとか)。でも、JanetがJPMでバイナリを作成できることや、スクリプトが書けてすごくポータブルなのは大好きだよ。昔、Janetプログラミング言語をPlaydateゲームコンソールにPOCとして載せたこともある。実際、Janetを書くのは楽しいんだけど、毎回書くとみんなが「この言語を作ったの?」って思うんだ(作ってないよ)。

Julia EvansがJuliaを使ってGunzipを視覚化する面白いブログ記事を書いてるよ: https://jvns.ca/blog/2013/10/24/day-16-gzip-plus-poetry-equa... 「JanetがJanetを書く」バージョンをやってみてほしいな。