ハクソク

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

言語設計をやめて、ライブラリを書くべきだ

概要

  • プログラミング言語の違いよりも、ライブラリの充実度が生産性に直結
  • Ruby on Railsのようなフレームワークは、言語特有の機能を活用している
  • 言語設計は、どんなライブラリが書けるかを決定
  • ライブラリの限界は言語の限界でもある
  • 理想の言語は、ライブラリ作者が必要な拡張を簡単に実現できるもの

言語設計をやめて、ライブラリを書こう

  • 多くのプログラミング言語は基本構文や機能が似通う傾向
  • 一般的なプログラマにとって、生産性を高めるのは使いやすいライブラリの存在
  • 例:Ruby on Railsによって、非専門家でも本格的なWebアプリ開発が可能に
  • 言語自体へのこだわりよりも、Railsのようなフレームワークに魅力を感じる意見が多い
  • 専門的な言語機能(例:ファーストクラス関数やコルーチン)は、多くのユーザーが意識せずに恩恵を受けている

なぜRailsのようなフレームワークは他言語で再現できないのか

  • Ruby on Railsは、Ruby特有のメタプログラミングやランタイム評価機能を多用
  • JavaやCなど、他言語のメタプログラミング機能の制限により、同様のフレームワーク構築が困難
  • 言語の設計がライブラリの表現力や使いやすさを直接左右
  • 例:C言語のライブラリは関数群中心、JavaのGUIライブラリはハンドラークラスの作成が必須

インタラクティブなソフトウェアとライブラリ設計

  • 初期のソフトウェアはバッチ処理が主流で、関数の再利用で十分
  • 近年はインタラクティブなアプリケーションが主流となり、拡張性の高いライブラリが求められる
  • JavaやC++はサブクラス化による拡張を提供、これが「フレームワーク」という新しい概念を生んだ

言語設計の動機とライブラリの限界

  • 新しい言語開発の動機は、既存言語で望むライブラリが作れないことへの不満が多い
  • 例:Javaでゲームライブラリを作ろうとしたが、コルーチンや継続の欠如で限界を感じた経験
  • Schemeのような言語は継続をサポートし、より直感的なゲームロジック記述が可能
  • しかし、型システムの拡張などはライブラリでは実現困難。主流言語で型システム自体をライブラリで拡張できるものはない

言語の本質的な役割

  • 汎用プログラミング言語の目的は、強力で使いやすいライブラリの実現を可能にすること
  • 言語が強力であればあるほど、ライブラリの表現力・使い勝手が向上
  • どんなライブラリが書けて、どんなライブラリが書けないかが言語の個性や限界を決定
  • 例:Stanzaはオプション型システムやマルチメソッド型オブジェクトシステムなどを提供
    • しかし、オブジェクトシステム自体をライブラリで差し替えることはできない

これからの言語設計とライブラリ

  • 未来の理想的な言語は、ライブラリ作者が型システムやオブジェクトシステムを自由に拡張可能なもの
  • RacketやShenは型システム拡張をサポート、メタオブジェクトプロトコルの研究も進行中
  • ライブラリの可能性と限界が、言語設計の主戦場となる

結論

  • 強力なライブラリは、多くの言語設計上の工夫や研究の成果
  • 使いやすいライブラリの背後には、言語機能の選択と設計思想が存在
  • もしライブラリの優雅さに感銘を受けたら、どの言語機能が使われているかを調べてみる価値
  • サブtleな言語仕様に興味がなければ、そのままライブラリの便利さを享受しても問題なし

Hackerたちの意見

: https://hackage.haskell.org/package/shh
: https://docs.haskellstack.org/en/v3.9.1/topics/scripts/
: https://wiki.nixos.org/wiki/Nix-shell_shebang ちなみに、依存関係のあるHaskellスクリプトにnixのシェバンを使うなら、impure inputsの代わりにhttps://github.com/tomberek/-を使った方がいいよ。キャッシュ評価ができるからね。俺はこのリポジトリを自分のGitLabアカウントにクローンしたんだけど、小さいし、絶対に変わらないはずだから。
強い(でも徐々に)型付けされたスクリプト言語を探してるなら、https://raku.orgをチェックしてみて。
nushell見たことある?これを使うと、以前は「本物」の言語を使わなきゃできなかったことが、ワンライナーでできちゃうんだ。 ちょっとした例を挙げると: ```shell ls | where type == 'file' | sort-by size | take 4 | each {|f| {n: $f.name, s: ($f.size | format filesize MB) }} | to json ``` 出力はこんな感じ: ```json { "n": "clippy.toml", "s": "0.000001 MB" }, { "n": "README.md", "s": "0.000009 MB" }, { "n": "rustfmt.toml", "s": "0.000052 MB" }, { "n": "typos.toml", "s": "0.00009 MB" } ```
ここ数日前にGoにシェバンのようなものを追加するトリックが共有されてたよ。興味があるかもね: https://lorentz.app/blog-item.html?id=go-shebang ディスカッションはこちら: https://news.ycombinator.com/item?id=46431028
数ヶ月待ってれば、そのプログラムは型付き言語で書かれるようになるよ。Elixirの型チェッカーは順調に進んでるし、リリースのたびにチェックが増えてるからね。
OCamlはこの意味ではスクリプト言語だね。実行可能ファイルを事前にコンパイルする必要はなくて、ソースファイルの先頭に`#!/usr/bin/env ocaml`って書けばいいんだ。(もっと現実的には、`#!/usr/bin/env -S ocaml -I +unix unix.cma`)ただ、1ファイルのスクリプトが自動的にインストールされる3rdパーティの依存関係を宣言する機能はないと思う。
Babashka(https://babashka.org/)はスクリプト用の面白いツールだよ。Clojureだから動的型付けだけど、データ指向のおかげで君の例みたいなものにぴったりだと思う。
@dev_l1x_be: 答えは新しい型付きスクリプト言語じゃないよ。インタープリターが何であるかを認識することだ。LLMはeval()だ。スキルはプログラム。YAMLはマザーボード。@unkulunkuluが言ってる通り、「ライブラリは最終的な言語」、言語は全ての基盤だ。まさにその通り。スキルは言語そのもの。インタープリターに何を理解させるかを教えるんだ。インタープリターが意図を理解すると、その区別は消える。@conartist6: 「DSLは曖昧... 言語とライブラリは対立する必要はない」って言うのも正しい。伝統的なDSL: パース -> AST -> 評価。LLMの「DSL」: 意図を読み取る -> 理解する -> 行動する。全て一つのステップ。文の途中でコードスイッチしても気にしない。RORのような意見が強いフレームワークやDHHのようなBDFLの問題は、一つの意見が間違った数になることだ! 誰も言ってない重要な洞察: 光の速さ vs キャリアピジョン。キャリアピジョン: LLMに呼びかけて、応答を得て、解析して、再度LLMを呼び出し、繰り返す。遅いし、ノイズが多い。往復するたびにトークン化で精度が失われる。光の速さ: 一回の呼び出し。俺は33ターンのStoner Fluxxを実行した -- 10キャラクター、たくさんの意見、ゲーム状態、手札、ルール、対話、ジョーク -- を一回のLLM呼び出しで。LLMは思考の速さで内部的にシミュレートする。シリアライズのオーバーヘッドなし。文脈を壊す往復なし。@jakkos、@PaulHoule: nushellとPythonはいいけど、まだパーサーのための構文を書いてるよね。もし理解者のために意図を書いたらどうなる? Bashは悲劇だ -- フットガンの引用、jqの体操、書き込み専用の構文。俺たちのパターン: YAMLで意図を書いて、必要なときにLLMが「クリーンなPython」に引き上げる。Postelの法則を型システムとして: 受け入れるものに対して寛容であること。意味的理解はナンセンスをキャッチする。なぜなら、あなたが「意味した」ことを知っているからであって、ただ「タイプした」ことだけではないから。証明と哲学: https://github.com/SimHacker/moollm/blob/main/designs/stanza...
PowerShellを考えたことある?オープンソースで、型付きだし、コマンドラインからも使えるし、リソースもたくさんあるよ。 https://github.com/PowerShell/PowerShell
うーん、そうかもね。成熟した言語やエコシステムは成熟してるから、新しい機能を無理やり入れなきゃいけないんだよね。一方でRaku(https://raku.org)は、引用、正規表現、PEGなどのサブ言語(スラング)の編み込みとして意図的に設計されてるから、自分のスラングを簡単に作れるんだ。例えば、https://raku.land/zef:lizmat/Slangifyを使えば、一般的なプログラミング言語の中でDSLをドロップダウン言語として使えるようになるよ。
いろんな言語があるけど、Rakuは即座に却下だね。個人的には、型は変数名に従うべきだと思うから。
10年前はScalaの大ファンだったんだ。「スケーラブルな言語」というアイデアは、DSLを型システム内で構築できるからすごく強力に感じた。でも、コミュニティがJVM上でHaskellのように使いたいって決めたときに、興味を失っちゃった。最近のWASMやGraalのような開発が、言語選択の柔軟性を提供してくれることを願ってる。Rustがウェブ開発の真剣な選択肢になってきてるのを見るのは嬉しいね。大抵はJSで十分だけど、必要なときに厳格な低レベル言語を使える選択肢があるのはいいことだ。
Haskellは普通、DSLとして使われることが多いよね。MetaではHaxlが使われてるし、TidalCyclesはHaskellで作られた別のDSLの良い例だと思う。ただ、レンズやオプティクス、マシン、パイプみたいな高階ライブラリは完全に不要だと思う。そういうのは解決したくない問題を抱えて、パフォーマンスにも悪影響が出るけど、少なくとも正しいし、問題に対してすぐにコードを投げられるからね。コード自体は正しさ以外は全部ダメだけど。
最近Scalaを学び始めたんだけど、めっちゃ好きだよ。機能的な側面もいいしね。君のコメントについてだけど、Scalaは他の使い方にも十分汎用的な気がするし、DSLにも確実に使えると思う。何が足りないと思う?
> コミュニティがJVM上でHaskellのように使いたいって決めたとき、熱意を失った。 それはコミュニティ全体じゃないよ、全然ね。ScalaをScalaのサブレディットで判断しないで。Scalaについて書かれている新しいことのほとんどは、型を使って難しい問題を解決することに関するものだから、そういう問題は尽きることがないし、何かしらの理由で楽しんでる人もいるんだよね。正直言うと、これがScalaの他の部分がどれだけ簡単で使いやすいかを示してると思う。人々が書く難しさは、深い型の魔法やモナディックコードでのエラー処理についてのことばかりだから。自分にとって価値がないときは、そういうことを避けられるよ。型にこだわる人たちは、少しでも不純物を受け入れたり、型で全てを解決する挑戦から一歩引いた瞬間に、Scalaの全ての利点を放棄してるって言うけど、ただの意地悪で門番みたいなもんだよ。Scalaは素晴らしい言語だし、Scalaをより良いJavaとして使うのは、シンプルで読みやすいコードを書くためにJavaから大きく進化したことだよ。Javaではできない、シンプルな関数型コードがOO重視の命令型コードを凌駕する場面を楽しめるからね。
ジョークを思い出した。問題があるんだ。そうだ、DSLを設計しよう。うーん、今度は二つの問題ができた。
それがScalaの一番の問題だと思うし、年を取るにつれて特定のライブラリ(特にユニットテストの分野)でも感じる。言語やDSLを追加しようとするからね。Scalaを学ぶだけじゃなくて、使うものや達成したいことによって、いろんなDSLも学ばなきゃいけない。ひどい例がいくつかあるよ。良い使い方もあるとは思うけど、当時印象的だったのは、関数型プログラミングを使ってHadoopのマップ/リデュースジョブを作ることだった。Scalaではワンライナーで済むのに、Javaでは五つのファイルやクラスが必要だった。でもほとんどのプログラミングタスクには過剰すぎる。Scalaで退屈なコードを書くこともできるけど、私の(限られた)経験では、Scalaの開発者は退屈なコードを書きたくないんだ。彼らはScalaを選んだのは、仕事に最適なツールだからじゃなくて、退屈だから自分のスキルを見せたかったから。残りの95%のプログラマーがそれを使わなきゃいけないことは無視されてるし。(しかも、これらはコンサルタントだったから、1年以内に去ってCTOになったりして、10年後にScalaを売った会社はまだその影響に悩まされてる)
型を使って派手なことをするのは昔は興味があったけど、今はもう触れたくもない。賢くなろうとするライブラリに何度も痛い目にあったから。Scalaは半分人気があった頃に何度か使ったけど、Javaに余計な機能がたくさん追加された感じだった。何を目指していたのかよくわからない。
プロジェクトパナマがCメモリモデルを使ったライブラリとの相互運用性を向上させることを期待してるけど、移行は簡単そうには見えないね。Arrow Javaはまだsun.misc.Unsafeを使ってネイティブメモリアクセスをしてるし。
Clojure(Script)を試したことある?それが必要なものかもしれないよ。リスプ系の言語でのボトムアッププログラミングは、問題を解決するために言語を拡張することを意味するんだ。ポール・グレアムが1993年の著書『On Lisp』で言ったように、「プログラムは一連の層として書かれ、それぞれが上の層のための一種のプログラミング言語として機能するボトムアップスタイル」だよ。ここにClojureの概念を説明したトークがあるよ。「Clojureにおけるボトムアップ vs トップダウンデザイン」というタイトルだね。
Scalaは素晴らしい言語で、実際、この記事の主張を覆す言語だと思う。Javaは「ライブラリを書け」っていう流れがあったけど、それが本当に負担になっちゃった。シンプルな言語の限界を乗り越えるために、たくさんの醜いライブラリやフレームワーク、パターンが作られたんだ。Scalaは、Javaエコシステムで使われている実績のあるデザインパターンやライブラリ機能を言語のコアに統合して、私たちにとっては良い結果になった。Javaでは依存性注入のためにSpring(うわぁ)を使わなきゃいけなかったけど、Scalaでは「given」キーワードがある。Javaでは、関数型プログラミングを面白くするためにGuavaが必要だったけど、FP機能はJavaのコアに徐々に追加されていった。でも、JavaのFPの力と表現力は、Scalaのコアやコレクションライブラリに比べると悲しいくらい劣ってる。JavaではLombokやビルダーパターンが必要だったけど、Scalaではケースクラス、名前付き引数、デフォルトパラメータ、そしてデフォルトで不変性がある。Javaエコシステムでは、オプショナリティがnull(うわぁ)や粗雑で一貫性のない「Optional」の混合から来ているけど、ScalaではOptionがコアにあって、自然に組み合わせられる。Javaではチェック例外がメソッドシグネチャに影響を与えるけど、ScalaではTry、Either、Validatedがある。エラーは値なんだ。もっと組み合わせやすいし、他にもたくさんあるけど、成熟したエコシステムとシンプルな言語であるJavaからベストを取り入れて、新しくてよりエレガントで完全な言語であるScalaを作ることには正当な利点があるってことは伝わったかな。
一番の例はPrologだね。論理プログラミングの典型的な代表としていつも挙げられる、珍しい言語パラダイムだ。でも、言語である必要はないんだ。実際には、どの言語にもライブラリとしてあるべきアルゴリズムの集まりで、Prologのことをその言語の構文で表現するためのいい規約があればいいんだよね。(ちょっと記事から外れたコメントだけど、タイトルには合ってると思う。)
特に優れたプロログのライブラリ実装の例があれば教えてほしいな。
SQLと同じように扱ってるよ。
他の人が挙げた例は、ただライブラリにすることの問題を示してると思う。Prologの多くの機能、特に最適化に関しては欠けてるんだ。Prologの機能が必要なら、普通にSWI-Prologを呼び出せばいいんじゃないかな。
でも、なんでそうなるべきなの?その点にはあまり触れてないよね。「Haskellは独自の言語じゃなくて、JavaScriptライブラリに統合されたラムダみたいな機能のいくつかであるべきだ」って言ってるようなもんだ。そうすると、JavaScriptにはいい機能が増えるけど、純粋な関数型プログラミングの価値提案が消えちゃう。それは実際の損失だよ。異なる特性や保証を持つ純粋な機能セットを維持することには意味があるんだから、「すべてが命令型言語のライブラリ」になっちゃうのはダメだよ。
ライブラリとして実装された場合、WAMや関連するJITの利点を活かせないんだよね。
その理屈でいくと、オブジェクト指向の機能なんて言語に必要ないよね。クロージャがあるから、ライブラリで同じ機能を実現できるし。たまには、独自の構文を持つ言語の方がいい場合もあるよね。
プロログのような構文をライブラリとして実現できる言語は、リスプ系の言語くらいしか知らないな。少なくとも、合理的なシンボル構文を持っている言語ね。変数を第一級オブジェクトとして扱えないと、使い勝手がめちゃくちゃ悪くなるよ。ボブ・ハーパーとこの特定の問題について話したことがあって(文脈はマクロシステムがなぜ重要かということ)、彼の答えは「別のプログラミング言語を書けばいい」だった。なるほどね。でも、これは単に言いたいことがあって、リレーショナルプログラミングをライブラリとして実現するのには、言語が特定の機能をサポートしていないとたくさんの問題があるってことなんだ。
プロログの主なポイントはアルゴリズムじゃないよ。実際、深さ優先探索は標準の一部でもないし。プロログの良いところは、論理ルールを書けて、それを必要な順番や方向で使えることなんだ。方向っていうのは、「祖父母は親の親である」と定義すれば、そのルールを使って一人が親かどうかを評価したり(祖父母を見つけたり)するだけじゃなくて、誰かが祖父母で、かつその人が誰かの親であれば、その人は誰かの親だと結論づけることができるってこと。再帰もできるから、先祖を親または先祖の親として定義すれば、家系図をさかのぼって再帰することができるんだ。面白いよね。Cコードを取り込んで出力から入力に強引に変換するようなランタイムを書くこともできるけど、通常の命令型コードはそれを不可能にするような副作用を許すから、結局は特定のサブセットに制限されて、またドメイン特化型言語になってしまうんだ。記事が言いたいのは、特定の言語の機能を使うと、他の言語にライブラリとしてコードを埋め込むのが難しいってこと。でも、本当にそうかな?DLLは同じ言語で書かなくてもいいし、JNIみたいなものもあるし、有名なところではPyTorchやTensorFlowがPythonからCUDAコードを実行することもあるよ。
Prologの構文は基本的に一階論理の言語のサブセット(量化子や関数記号なし)だから、これ以上ミニマルなものはないよ。命令型言語や関数型言語と比べてPrologの特別なところは、変数が「代入」されるんじゃなくて、文脈を満たすまで潜在的な値の範囲を暗黙的に持つところだね。数学の集合の式みたいに。確かに、型注釈やDSLの慣習をたくさん使って、好きなプログラミング言語を離れずに表現することもできるけど、Prologの「エンジン」は、同期ライブラリ呼び出しの背後で合理的に想定される以上のことをやる問題があるんだ。コンパクトな解空間の表現や値のファクタリング、並列実行環境、自動的な値の剪定や伝播などね。Prologバックエンドを主流のスタックに統合するのは、通常Prologコード生成(LLMを使ったコード生成も含む)やProlog側の「サービス」として行われるよ。PrologはDSLの解析やあらゆるタイプのリクエスト/レスポンスのサポートも優れているから、実際には1行のコードでJSONパーサーを実装できるんだ。言うまでもなく、Prologがアプリケーションに合うなら、本当にうまくフィットするよ。計画、制約解決、定理証明、検証/組合せテストケースの列挙、価格モデル、法的/戦略的なケースの差別化、複雑な構成などでね。後者は、独立したユニットを使って複雑なプログラムを構成する際に論理句のモジュラリティを活用するだけなんだ。だから、君がPrologをどれだけ実際に触ったかは分からないけど、実際には一番良い例じゃなくて一番悪い例を選んじゃったんじゃないかな ;)
Prologって、うまく動くと魔法みたいで最高だよね。プラグマを渡すと、答えを教えてくれる。逆に、うまくいかないときも魔法みたいに感じるのが難しいところ。どれだけの作業が必要かを考えるのが難しいんだよね。もしPrologのプログラムがすごく時間かかってるなら、2時間か4,000年か、全然わからないし。
> Javaの開発者はRubyプログラマーほど有能じゃないの? 何年か前に、近くのシニア開発者が「面接でRailsを見たら、履歴書をゴミ箱に捨てる」って言ってた。こういう言語に基づく判断がどれだけトリビアルでバカバカしいか、皮肉だよね。
数年前、同僚のゴミ箱を集めるように頼んでたよ :) 近くでは見つけるのが難しい、少なくとも良いものはね。これはどの言語や職業にも当てはまることだけど。俺は履歴書にRoRを書いてるし、すごく好きなんだ。
> 数年前、近くのシニア開発者が言った「シニアのスタックは何だった?」
一方の脳は、そんな判断の仕方はバカだと思うけど、受け入れられているフィルタリングメカニズムを考えると、実際にはそれほど良くも悪くもない。全員を面接することはできないし、最終的には能力、適合性、意欲、社会的フィット感の組み合わせを探している。以前働いていた場所や学校、コーディングチャレンジに合格できるかどうかでフィルタリングするのは、マトリックスの一部を埋めるだけなんだ。組織の大きさや形によって、採用プロセスに関わる人たちが各データポイントを見る頻度も変わる。このシニアエンジニアの理想は、同じように考える人や彼らの部署のトップを好んでいたんだろうね。
Javaの開発者を探しているなら、Railsの履歴書は捨てるのが一番だよ。お互いの時間を節約できるしね。
公平に言うと、何年も前にRailsは「ブートキャンプ」のプログラマー養成所が推していたものだったよね。君はその全体の文脈を知っているけど、それがないとRailsが質の低い応募者と結びついていると見なされてしまうのも無理はない。Rails自体に問題があるわけじゃなくて、そういう「ブートキャンプ」が質の高い人を育てていなかったからなんだ。どこかで選別は必要だよね。もし素晴らしい開発者を捨ててしまったとしても、仕方ない。彼らのことを心配する時間はないから。
Railsの台頭とJ2EEの衰退が同時期に起こったのは、偶然じゃないと思う。Railsに特別な思い入れはないけど、シンプルで意見がはっきりしたバックエンドコードの書き方を示してくれたから、それに触発されてJava/JVMのウェブフレームワークがたくさん生まれたんだ。最終的にはDropWizardやJavalin、SparkJava、さらにはSpring Bootのようなライブラリも生まれたよ。
一方で、これが多くの有資格者をフィルタリングするのは明らかだよね。もう一方で、1日のうちにできることには限りがある。どんなエンジニアリングの役割でも、来る履歴書に電話スクリーニングをするのに3〜6ヶ月かかるし、4時間のフルインタビューだとその8倍かかる。月に数人を雇うのが仕事なら、何らかの形でフィルタリングしなきゃいけない。まずはあまり物議を醸さないものから始めるよね。履歴書の半分は明らかにボットスパムだし[0]。残りの半分は、コアの職務に関連するものが全くない履歴書で簡単に捨てられる[1]。それでも、まだたくさんの履歴書が残っていて、電話スクリーニングできる数を超えてる。何を基準にスクリーニングする? - 「良い」学校?パフォーマンスに関してはあまりにもばらつきがあるから、これをフィルターとして使いたくないし、FAANGとの給与競争もさらに厳しくなるし。 - 良い成績?これは初期キャリアの役割には良い指標だけど、まだかなり弱い信号だし、介護者として休暇を取った人や、成熟する前に始めた人を罰することにもなる。 - 取得した最高学位?これがどんな選択バイアスを引き起こすのか分からないけど、面接や仕事でパフォーマンスが悪い博士号取得者が多いから、履歴書スクリーニングの段階で博士号取得者をフィルタリングするために使うかもしれない。 - 性別?年齢?… こういうことがあるのは知ってるけど、やめてほしい。強いGitHubプロファイルがあれば、その人をスクリーニングに進めるのは簡単だけど、他の履歴書を捨てるのはフェアじゃない。彼らには仕事、スキル、成果のリストがあって、それを使って面接のラウンドを通過する可能性が高いかどうかを判断するのが君の仕事なんだ。Railsについては特にコメントはないけど、低レベルのML役割に関しては、あまり強調してほしくないスキルがあるのは確かだよ。悪いからではなく、そのスキルを学んだだけで他に何も学んでいない大きなクラスの人々がいて、彼らが候補者プールを支配しているから。以前はそういう履歴書にもチャンスを与えてたけど、電話スクリーニングがフルインタビューやオファーに変わる確率が100:1では受け入れられない。候補者にとってもフェアじゃないし、私にも時間がない。これは…悪いよね?いくつかの方法で改善しようとしてるけど(他の面では悪化することもあるけど、平均して人々の時間を節約して、あまりにも多くの有資格者を拒否しないように) -- チャンスがあると思ったら、履歴書を outright reject するのではなく、(簡単な)書面でのスクリーニングに回すようにしてるし、間違いがあったと返事が来たら必ず電話スクリーニングを行うようにしてる。時々、構築したフィルタリングルールを見直して、電話スクリーニングを提供することもあるしね。とはいえ、採用は両方の側でまだ厳しいよ。[0] 私のお気に入りの一つは、彼らの「経験」に、どうやら職務記述からコピー&ペーストした超特定のタスクをやったことが含まれていることだね(これはスキル要件としてではなく、彼らの未来の日常がどうなるかの説明として存在している)。彼らは私たちがその技術を開発する前にそれをやったし、いくつかのFAANG企業でそれをやったと言っているけど、実際にはその企業が使っていない言語やツールを使っていたり、FAANGでの在籍中には存在しなかったりする。もしかしたら、彼らは履歴書を整えるためにLLMを誤って使ったのかもしれないけど、私が君を面接する理由が詐欺だらけの履歴書だけだとしたら、疑いの余地を与えるつもりはないよ。[1] 特定の言語やフレームワークを要求したり、データベース関連の役割でデータベースとやり取りしたことが
俺にとってタイトルはちょっと矛盾してる気がする。ライブラリは「最終的な言語」だと思ってるから。著者のRoR/Rubyの例は「RoRはRubyをベースにした素晴らしいウェブサービス言語で、二つは共に進化してきた。RoRがRubyのクライアントの主要なソースだから、RubyもRoRのために設計されたと言える」って感じ。プログラミングやデザインは言語や翻訳だと思ってるから、結局は言語が全てだよね。
それに同意するよ。C#とASP.NET Coreも共同開発されたし、面白いことに、ユーザー向け(型推論がたくさんあったり、ボイラープレートを減らしたり…)とシステム向け(低レベルのパラダイムで高最適化のウェブサーバーを書く)両方に向かって進化してるよね。
どの言語やライブラリも、機械と人の両方にコミュニケーションする必要がある。マイクロレベルでは、機械はビットや電圧などで動作する。これは技術的な問題で、簡単ではないけど、機械的には処理可能だ。一方で、人はアイデアやメタファー、意図の表現などで動く。もし言語やライブラリがそれらのコミュニケーションをより簡単、良く、早くしてくれるなら、明確に「書き下ろせる」し、明確に「読める」なら、どの分類に入るかなんて本当に重要かな?我々は目的に応じて馬を選ぶ。それが正しいと思う。もしRailsがあなたに合っていて、達成したいことと補完し合い、加速器の役割を果たし、周りの人にもよく理解されているなら、それを使えばいい。逆に、すべての条件に対する答えがStanzaなら、それを選べばいい。そういう決断には「正しい」や「間違っている」よりも「進む」や「もがく」の方が重要だ。ちょっと陳腐に聞こえるかもしれないけど、使えるものを使おう。もし何かがうまくいかないなら、うまくいくものを作ればいい。それが最も効率的なアプローチならね。
これって、部分的には誤った二項対立に見えるな。言語をデザインしたり、ライブラリを書いたりできるし。中には「ライブラリ」の一部は言語そのものと見なせるって主張する人もいるし、逆に言語が他の言語のライブラリとして書かれていることもあるよね。言語は実装(コンパイラやインタプリタ)から孤立しているわけじゃないし。理論では言語を意味論で見ているけど、実際にはOPが指摘しているように、実装の質やその言語で何ができるかが重要なんだ。最近の例としてはJuliaがあるね。言語デザインの「ちょうどいい」ポイントに達していて、新しいパフォーマンスの高いコードは他の言語よりもJuliaで書かれることが多くなってる。基本的には「関数を呼び出して、データを渡して結果を得る」みたいなシンプルな言語だけど、関数(「メソッド」)の意味や、コンパイラが深い型に特化したコードで事前コンパイルを行う方法のおかげで、高レベルなコードを効率よく書けるんだ。そういうメカニズムは裏で動いているから、プログラミング体験が快適になるし、Juliaでは最先端のパッケージがたくさん書かれているよ。Juliaを「ただの言語」として見るのはあまり面白くないね。最近のプログラミング言語の話題は、たぶん15年以内のことかな?言語の可能性を発見するには時間がかかるから。
> これは部分的に誤った二項対立に見えるけど、記事のポイントはそこじゃなかった?両方必要だってこと?
新しい言語を書く方法はいくつかあるよ。一つは、著者が主張しているように、既存の言語でライブラリを書くこと。これはホスト言語の構文に合わせるために使いやすさを犠牲にすることがあるけど、安全でモジュール化されて再利用可能だよ。多くのDSLはコンパイラ拡張のサポートがある既存の言語に組み込むことができる。このアプローチは柔軟性が高いけど、しばしば言語エコシステムの断片化や相互運用性の低下を招くことがある。ミネソタのグループが確立した第三のアプローチは、最初からモジュール式で拡張可能な言語やツールを設計すること。これにより、拡張がより相互運用可能になるんだ。彼らは属性文法を使ってこれをどう実現するかを研究しているよ。ホスト言語が十分に表現力のある型システムを持っていれば、流暢なAPIや型安全な埋め込みDSLを書くことができることが多いけど、メタプログラミングをサポートする良い型システムや言語を設計することも活発な研究分野なんだ。[3, 4] もしこれらのオプションがどれも機能しないなら、最後の手段はタブラ・ラサから始めて、自分のパーサー、コンパイラ、開発ツールを書くことだね。これが最も柔軟性があるけど、膨大なエンジニアリングが必要で、2026年には一般的に推奨されないよ。
> ミネソタのグループが確立した第三のアプローチは、最初からモジュール式で拡張可能な言語やツールを設計すること。これにより、拡張がより相互運用可能になるんだ。彼らは属性文法を使ってこれをどう実現するかを研究しているよ。MLIRが登場したね :P 知ってる、MLIRはIRであってプログラミング言語じゃないけど、MLIRは「すべてを支配するIR」って感じがするんだ(SSA表現に問題がなければね)。しかも、IR自体はかなり高レベルで、ほとんど実際のプログラミング言語みたいに感じるよ。例えば、EmitCダイアレクトを使ってCにコンパイルするMLIRプログラムを書くことができて、Cを書くのと似た感覚になるんだ。
> もしこれらのオプションがどれも機能しないなら、最後の手段はタブラ・ラサから始めて、自分のパーサー、コンパイラ、開発ツールを書くことだね。これが最も柔軟性があるけど、膨大なエンジニアリングが必要で、2026年には一般的に推奨されないよ。これみたいな感じ? https://github.com/williamcotton/webpipe https://github.com/williamcotton/webpipe-lsp https://github.com/williamcotton/webpipe-js まあ、これを言うと、少し楽になるかもしれないけどね…
著者が言ってることは、言語の機能が特定のライブラリを書くことを可能にするっていう良いポイントだよね。DSLが問題を考えるのを楽にして、適切な抽象化や言語の使いやすさで解決策を実装するのを助けるのと同じように(通常は表現力や柔軟性を犠牲にして)。私の人生の中で、言語を設計してコンパイラを書く時期があったよ。非技術的なユーザーにもアプローチしやすい言語として、英語のような構文を持つアウトライン風の言語が作れるんじゃないかと思ってる。アウトラインの形は非常に固定されていて、ガードレールがあって、普通のプログラミング言語のように任意の命令を表現することはできないけど、上級ユーザーのために(より表現力のある言語への)エスケープハッチを提供できるようにする。これを使う場面としては、一般的なポータル管理アプリの生成やワークフローの自動化が考えられるかな。ただ、AIアシスタントの登場で、私のDSLアイデアにまだ余地があるかは分からないけどね。