くやしいおじさんが言語サーバーを試す

2026/08/26 21:48

くやしいおじさんが言語サーバーを試す

RSS: https://news.ycombinator.com/rss

要約

Japanese Translation:

本文は、Haskell 開発者がアプリケーションを再起動せずに Lisp 風のライブリローディングを実現するための専用ワークフローを概説する。従来の Haskell 開発はユニットテストに依存し、Lisp のような実行プロセスの改変や条件システムに対するネイティブなサポートを欠くが、本アプローチは

ghcid
foreign-store
というツールを活用して、コード変更がアクティブなメモリセッション内で直ちに適用される動的環境を模倣する。著者は安定性の問題に苦しむ既存の選択肢(例えば Rapid ライブラリなど)ではなく、IORefs および Async を用いてカスタムパイピングを実装した。この設定により、アプリケーション状態を保ちながら高速な反復が可能となり、Haskell の静的な性質と Lisp の歴史に伴う流体開発体験との間のギャップを埋める。有効ではあるものの、現在の手法はすべてのソースコードが単一の実行可能ファイル内に存在する必要がある。これは基盤となる
ghci
ツールが同時に 1 セットのファイルのみを追跡できるためである。安定した環境を維持するためには、Emacs および Eglot と hls(コードの introspection 用)といった開発エディタを設定するとともに、リローディングサイクルに干渉する可能性のある Flycheck のような機能を無効化しなければならない。リロード中における関数の動作を検証するためには、REPL の探求に依存するのではなくユニットテストが用いられる。

本文

Lisp 開発者のスタイルを Haskell で再現する試み:ライブリロードと REPL 近似的なワークフロー

私は現在、10 年前のスタイルでコードを書いていますが、実際のキー入力はロボット(自動生成ツールなど)に任せるようになっています。ただし、以下の例外は残しています。

  • コード品質に厳しく関心がある場合
  • 新しいことを学ぼうとしている場合

これらは、「エディタ編集 → ターミナルでのコンパイル・実行 → 結果確認」というサイクルを踏みます。何か分からない場合は**デバッグ用の出力(tracer print)**を追加するか、デバッガー付きで再起動します。プログラムがクラッシュすれば、修正して再度起動します。

Lisp 開発者のアプローチとは?

Lisp 開発者のスタイルには憧れを抱いています。彼らの特徴は以下の通りです。

  • プロセス内での作業: 「切り替え」する必要がありません。プログラムのプロセス内部に常にいます。
  • ホットスワップ (Hot-swap): コードをコンパイル・実行する必要はなく、コードそのものが実行中で、書き換えながら動作させます(追加・削除・置換)。
  • デバッガーでの再起動不要: プロセス内部にいるため、必要な箇所を自由に見ることができます。
  • 例外後の即時再開: 条件システムにより、修正パッチを適用した後のスタックの任意の場所で実行を再開できます。

このスタイルでは、プロジェクト初期段階にソースコードそのものが存在しないこともあります。システムの定義はファイルではなく、実行中のプロセスのメモリーイメージにあります。これは「リレーショナルデータベースが実行状態にあるスキーマで始まる」ことと同じく、人々にとって理解しにくい慣習です。

成熟した段階では、メモリーイメージからソースを出力してバージョン管理へ移行しますが、初期段階はすべてが**「生きている状態」**で進化するのです。私は Haskell でその体験を得ることはできませんが、それに近づくために試行錯誤しています。

Haskell における可能性と制限

Lisp 開発者にならないが、Lisp 風のワークフローに近い部分を実装することは可能です。

即時勝利 (Partial Wins)

  • 型システム: 強力な型システムのおかげで、例外を引き起こすことが比較的少ないです。
  • LSP (Language Server Protocol): Emacs の Eglot クライアントを通じて、高品質なコード検査(introspection)が可能になりました。
  • ghcid と状態維持:
    ghcid
    は変更を検知して即時再コンパイルを行い、関数を実行させます。これを
    foreign-store
    ライブラリと組み合わせることで、プロセスの状態を失うことなく自動再起動が可能です。

制限事項

  • REPL での逐次評価:
    ghcid
    は実行中の REPL でコードを評価する機能をサポートしていません(これは Haskell では不可能)。
  • 代替アプローチ: 代わりに、検証すべきコードを実際の単一テストケースとして書き、
    ghcid
    がリロードする際にそのテストも実行させる方式を採用しています。

試作プロジェクト (The Toy Project)

偏微分方程式や非線形力学の学習用に自作したスロープフィールド(斜率場)可視化ツールです。

  • 目標: 本を探索しながら、コード・テスト・保存を行うだけでウィンドウが自動的に最新変更を反映したい。
  • 結果: Lisp 風ではないものの、前述の技術により良好な体験を提供できました。

Emacs と HLS の連携設定 (Eglot)

Emacs で Haskell Language Server (HLS) を動作させるための手順です。新規プロジェクトでのゼロから構築が必要です。

プロジェクト構成

  • cabal init
    : 標準的な Haskell プロジェクトを作成(実行可能部・ライブラリ・テストに分ける)。
    • 注意: GHCi は複数の部品からなるプロジェクトの再ロードで失敗することがあるため、すべてのコードを一箇所に配置した単一可執行プロジェクトに変更するのが確実です(本番環境では推奨されないが試作には適する)。
  • Nix (flake.nix):
    cabal
    プロジェクトをラップし、依存関係管理を行うサンドボックス環境を作成します。システム全体へのインストールは不要。

direnv の統合

  • direnv
    : シェルユーザープロファイルに統合し、プロジェクトディレクトリ内の
    .envrc
    (
    use flake
    ) を介して Nix 開発シェルの依存関係を自動的にロードします。
  • Emacs 連携: Emacs で
    direnv
    と連携させることで、Eglot が HLS サーバーに接続できるようにします(グローバルインストールがない場合でも動作)。
    • トラブル: 最初は手動でエンスローを起動する必要がありましたが、
      .envrc-global-mode
      の有効化や
      envrc
      フックの設定により解決しました。

Eglot の動作確認と設定

  • 接続:
    M-x eglot
    で接続。関数呼び出しへのカーソル移動 (
    xref-find-definition
    ) などで機能を検証。
  • エラーハンドリング: スタックオーバーフロー時に手動で
    M-x eglot-reconnect
    を実行する必要がある場合があります(サーバー再起動には時間がかかるため)。
  • 自動接続:
    haskell-mode
    eglot-ensure
    フックを追加し、Haskell コードを開いた際に自動的に接続しますが、遅延やエラーを避けるために成功確率が高い場合に限定しました。
  • Flycheck: HLS が役割を果たすため無効化(または必要最小限に)。
  • UI 設定:
    • eldoc-echo-area-use-multiline-p
      nil
      にして、ポップアップを非表示にし、カーソル下に型シグネチャのみを表示。
    • Vim のような素早い移動は信頼性に欠けるため、慣習との相性には課題あり(マニュアル読解が重要)。

ghcid と foreign-store によるライブリロード

GHCi は同時に一つのソースファイルセットしか追跡できないため、実行可能部・ライブラリ・テストを別々のコンポーネントにするとうまく動作しません。

  • 対策: すべてのコードを一つのコマンドライン引数で再ロード可能な構造にまとめる(試作プロジェクトでは許容)。

コード構造と機構

以下のスクリプトは、テスト実行後に更新関数を呼び出し、プロセス全体を再起動しながらリソースを再利用します。

module Main (main) where

import Control.Concurrent
import Control.Concurrent.Async
import Control.Exception (bracketOnError)
import Data.IORef
import Foreign.Store
import Test.Hspec (describe, hspec, it)
import Test.Hspec.QuickCheck (prop)

-- 再ロード時に実行されるメイン関数。
-- まずテストを実行し、成功すれば更新関数を呼び出して処理スレッドを再起動しつつ
-- リソースを再利用します。
main :: IO ()
main = do
  spec
  update

-- 例示ベースとプロパティベースのテストを混在させた単一テスト。
-- これらは REPL で試す代わりに書かれます。
spec :: IO ()
spec = hspec $ do
  describe "forward euler solution" $ do
    prop "ever increases with example study" $ \x y ->
      -- ...

-- プロセスが実行されていない場合は起動し、実行中の場合は再起動(リソースを再利用)します。
update :: IO ()
update =
  let
    -- SDL を実行する必要があるリソースを含む MVar です。
    -- また同時にロックとして機能し、複数のプロセスの同時実行を防ぎます。
    resourceStore = Store 0

    -- 更新中にキャンセルするためのプロセススレッドのアシンクロン(Async)を含むストアです。
    asyncStore = Store 1

    -- リソースが利用可能になるまで待ち、それを予約して、それらを使用して新しいスレッドを起動します。
    withResources action = do
      withStore resourceStore $ \resources -> async $
        -- 実行中のアシンクロン例外(例えばこのスレッドが更新手順によってキャンセルされた場合)をキャッチします。
        -- この例外は既に動作関数の停止を引き起こすため、その後にリソースを解放します。
        bracketOnError
          (putStrLn "Running action." >> takeMVar resources)
          (\res -> putStrLn "Action interrupted!" >> putMVar resources res)
          action

    start = do
      -- リソースを待ち受け、その後それらを使用してレンダリングループを実行します。
      withResources $ \(window, renderer, texture) -> do
        renderLoop renderer texture (State study Nothing)
        putStrLn "Render loop exited naturally."
        -- コントロールフローがこの点に達した場合、レンダリングループは非同期例外を経由せずに終了したことを意味します。
        -- これはユーザーが終了を要求した(例えばウィンドウの閉鎖など)ことを示しています。
        -- その場合リソースを解放するのではなく破棄すべきです。
        destroySdl window renderer texture
        -- さらに、次回 update 関数が呼び出される際に空の状態として見られるように、すべてのストアも削除する必要があります。
        deleteStore asyncStore
        deleteStore resourceStore
  in do
    lookupStore (case asyncStore of Store i -> i) >>= \case
      Nothing -> do
        -- スレッド ID ストアが存在しない場合は、真っ白な状態から開始していることを意味します。
        -- リソースを新たに初期化し、それらを使用する新しいスレッドを起動すべきです。
        initialiseSdl >>= void . storeAction (Store 0) . newMVar
        start >>= void . storeAction (Store 1) . newIORef
      Just tidStore ->
        -- スレッド ID ストアが存在する場合、参照しているスレッドをキャンセルし、それが使用したリソースを取得した後、新しいスレッドを起動します。
        -- 新しいスレッドはストア内に存在する任意のリソースを使用します。
        withStore tidStore $ \ref -> do
          readIORef ref >>= cancel
          start >>= writeIORef ref

実装のポイント

  • Rapid
    ライブラリよりも、
    IORef
    Async
    を用いて自分で配管(plumbing)を実装
    した方が、安定性とリソースのクリーンアップ、stdout サポートにおいて格段に優れていました。

起動コマンド

以下のコマンドは

ghcid
を起動し、自動再ロードと開発用メイン関数の実行を行います。 (注:これは「混乱による漸増」の結果であり、完全に理想的な方法ではありませんが動作します)

ghcid --command "cabal repl --repl-options='-fobject-code -ferror-spans -fdiagnostics-color=always'" \
      --reverse-errors \
      --reload src \
      --restart diffeq.cabal \
      --test Main

まとめ

  • ghcidforeign-store の組み合わせにより、コード内の定数を更改すれば可視化が更新され、本から新しいことを学べばそれ用のコードを追加し可視化が再構築される体験が可能です。
  • 完全な Lisp 風の REPL 評価はできませんが、テスト駆動型のアプローチでその代替手段を構築しました。
  • 初期段階の試作プロジェクトにおいて、**「エディタを離れる必要がない」**という快適さが得られました。
  • よりスムーズにするためには、マニュアルを読み込み、設定の鋭い角(遅延や接続エラー)を理解・解消することが不可欠です。

同じ日のほかのニュース

一覧に戻る →

2026/08/28 2:17

1.1.1.1 の DNS キャッシュ最適化でメモリ使用量を 100TB 削減

## Japanese Translation: Big Pineapple は、5 つの主要なストレージ最適化によりパーエントリーフッタープリントを 50% 以上削減したことで、DNS インフラストラクチャを大幅に革新することに成功しました。`Vec` と `String` フィールドをボックスポインタに置換し、レコードセクションを単一のリストへ統合し、不要なオーナーフィールドを除去し、大きな列挙型バリアントをボックス化してパディングを削減し、レコードを連続的なワイヤフォーマットバッファに格納することで、膨大な量の無駄なヒープ容量を排除しました。これらの改善は、特に 1 クライアントネットワークにつき複数の照会バージョンをキャッシュする必要がある ECS クライアントサブネット(ECS)照会を処理する場所にとって極めて重要です。2026 年 5 月 18 日から 7 月 6 日までのプロダクションロールアウト後、システムは劇的な効率向上を達成しました:平均居住メモリは 9.3 GB から 5.3 GB に低下し(p99 使用量の 43% 削減)、実質的に Gen 13 サーバー 130 機以上の RAM を解放しました。また、挿通スループットが 43% 増加し、照会遅延は 19% 減少することで、高負荷下でも船団がより信頼性高く動作すると同時に、大きなコスト削減を実現しました。

2026/08/28 0:56

小型モデルがやってきました

## Japanese Translation: 本質的なメッセージは、人工知能の最近の進歩により、運用コストが劇的に削減されたため、Luna などのコスト効率の高いモデルが消費者向けサブスクリプションにおいて実現可能になったという点です。以前は、高トークンコスト(旧モデルでタスクを実行する際に約$1 かかっていたのに対し、新しいモデルでは約$0.10 で済むというシナリオが例)が障壁となり、消費者にとって月次 AI プランは数学的に不可能でした。新しい技術により、企業は破格な予算なしに強力な AI にアクセスできるようになり、その結果として消費者向けグレードのサブスクリプションが登場しました。 企業の大部分の仕事は「トークン・スパイラー(大量生成型)」のカテゴリーに属しており、画期的な新しさを優先するよりも速度と応答性を重視しています。日常業務の約 95% は深い発見ではなく実行上のロジスティクスを占めています。Fable などの最先端モデルは稀な研究ブレイクスルー(「IQ 180」)には不可欠ですが、将来の市場はこの高コストのイノベーション用ツールと、標準的な対話向けの低価格で効率的なオプションの間で分かれることになります。現在の成功には、企業環境において安全性を確保し、ロール管理を行い、プロンプトインジェクション攻撃を防ぐための新しい「ハーネス(枠組み)」を構築することにかかっています。この変化により、企業は標準的なタスクのために高価な人間による「革新的思考者」を、応答性の高い AI エージェントで置換できるようになりました。結局のところ、この進化は AI を排他的な贅沢品から、日常業務用の実用的かつ手頃な価格のユーティリティへと変革させ、ついに大衆にとって消費者向けグレードのサブスクリプションが現実的となりました。

2026/08/27 23:08

メカニカル・ムーブメント No.507

## Japanese Translation: 本プロジェクトでは、507 の異なるアニメーションの完全なコレクションを編成することを目的としていますが、アーカイブからいくつかのエントリが現在欠落しています。利用者がこの拡大するライブラリをナビゲートできるよう、色分けされたサムネイルで完成作品と未完成のものを視覚的に区別し、右上に配置されている「prev」と「next」リンクにより、サムネイルページの間の簡単な閲覧が可能になっています。デジタル倉庫は、ヘンリー・T・ブラウンによる元のイラストをクラシックな技術書の参考資料と並べて収録することで歴史的意義を備えています。将来の発展には、507 の完全なセットが達成されるまで新しいアニメーションを順次リリースするものがあります。チームはタイムラインについて定期的に Facebook や Twitter などのソーシャルメディアプラットフォームで情報を共有することで透明性を維持しています。最終的には、ユーザーは現代的なデジタル作品と本物の歴史的アートワークの両方にアクセスできる利点を享受でき、プロジェクト自体に関する情報も「About」ページで入手可能です。