HN 日本語サマリー

← 一覧へ戻る
プログラミング

気難しい人が言語サーバーを試す

A curmudgeon tries a language server (entropicthoughts.com)

25 pointsby crescit_eundo3 コメント

要約

長年テキストエディタとターミナルでの開発スタイルを続けてきた筆者が、Haskell言語サーバー(LSP)とghcid、foreign-storeライブラリを組み合わせた開発フローを試した体験談です。Lisp開発のようなライブコーディング体験に近づくことを目指し、コードの変更、テスト実行、状態維持を自動化する新しいワークフローを構築しました。

全文翻訳

私は、10年前とほとんど同じ方法でコードを書いています¹¹この記事の執筆を開始した時点では、これは真実でした。その後、プロジェクトで(a)コードの品質を気にする場合、または(b)何かを学ぼうとする場合を除き、実際のコード入力をより多く行うロボットを採用しました。テキストエディタで編集を行い、ターミナルウィンドウに切り替えて、コードをコンパイルして実行するコマンドを実行します。結果を見て、エディタに戻ります。何が起こるか確信がない場合は、コードにトレースプリントを追加してプログラムを再起動するか、デバッガでプログラムを再起動します。プログラムをクラッシュさせる例外に遭遇した場合は、問題を修正してから再起動します。 Lispプログラマーが何をするかに憧れています。Lisp開発は通常、ライブで実行中のシステムの部品を追加、削除、置換するために、コードを直接REPLに入力することによって行われます。²²実際には、Lispプログラマーはおそらくエディタのスクラッチパッドにコードを入力してからREPLに送信しますが、それはあたかもコードを直接REPLに入力したかのように思えます。Lispプログラマーは、すでにプログラムプロセス内にいるため、「何か別のものに切り替える」必要はありません。コードはすでに実行されており、ホットスワップでコードを編集するため、「コンパイルして実行する」ことはありません。すでにプログラムプロセス内にいて、何でも検査できるため、デバッガで再起動する必要はありません。システムのパッチが修正で適用された後、スタックのどこからでもクラッシュしたコードを再開できる条件システムがあるため、例外でプログラムを再起動する必要はありません。 この作業方法の結果として、Lispプロジェクトの初期段階では、ソースコードと呼べるものがほとんどない場合があります。代わりに、システムの進化する定義は、実行中のプロセスのメモリイメージにのみ存在し、どこにもありません。これは、私たちが通常行う開発とは非常に異なるため、人々はその意味を理解するのに苦労すると表明しています。助けになる比較は、プロジェクトの開始時に人々がリレーショナルデータベースをどのように扱うかを考えることです。多くの場合、人々はバージョン管理されたスキーマ定義から始めません。むしろ、進化するスキーマは実行中のデータベースにのみ存在します。確かに、プロジェクトが成熟すると、データベーススキーマはファイルにダンプされ、バージョン管理に入れられ、それ以降、スキーマの変更は明示的なマイグレーションを通じて行われます。同様に、成熟したCommon Lispプロジェクトは、最終的にメモリイメージからソースコードにダンプされ、そのソースコードはバージョン管理に入れられ、パッチはライブシステムにより慎重に適用されます。しかし、初期段階では?ああ、それはすべてライブで進化しています。 私はLisp開発者ではないので、その経験をすることはありません。私は憧れ続けます。しかし、私はそれに近づこうとさえしていないことに気づきました。Haskellという私の作業言語でも、いくつかの改善が得られるかもしれません。Haskellで何ができるでしょうか?すぐに除外できるものと、あまり労力をかけずに部分的な勝利になりそうなものがあります。Haskellをプログラムプロセス内に入れる方法はないため、デバッガで再起動する必要は依然としてあります。HaskellでLispのような条件システムを実装することは可能であると確信していますが、一般的なライブラリで使用される例外(EitherTおよび非同期例外)にはそのようなものはありません。一方で、強力な型システムのおかげで、Haskellでは例外につながることは少なくなります。これは、指一本動かさずに得られる部分的な勝利です。 数年間、Haskell言語サーバー、つまりHaskell用のLSP実装が存在しています。最近では、Emacsは組み込みのEglotクライアントを通じてLSPを話すための組み込みサポートを持っています。これは、私が慣れているよりも高品質なコードイントロスペクションを得るための、もう一つの安価な部分的な勝利のように思えます。 次に、Haskellソースファイルの変更を監視し、変更されたものを即座に再コンパイルするghcidというプロジェクトがあります。それだけでは、hlsがすでに提供している以上のものは得られませんが、ghcidはコードが正常にコンパイルされた後に関数を実行することもできます。これは強力です。なぜなら、それはforeign-storeライブラリ(またはより高レベルのRapidライブラリ)と組み合わせて、すべてのコードが置き換えられた場合でもプロセスの状態を維持できるからです!したがって、Lispプログラマーが定義をREPLに段階的に送信するのに対し、私たちはghcidとforeign-storeの組み合わせを使用して、重要な状態を失うことなく、プログラム全体を自動的に再コンパイルして再起動します。これらの2つのアプローチは、多くのクラスのソフトウェアで同様のワークフローにつながるはずです。 私たちがまだできない唯一のことは、実行中のプログラムのREPLでコードを評価することです。なぜなら、ghcidはそれをサポートしていないからです。しかし、私は主に関数が意図したとおりに機能するかどうかをテストするためにこれを行うので、代わりにできることは、そのコードを実際の単体テストケースとして書き、ghcidがコードをリロードするときにテストも実行するようにすることです。私たちは、エディタでコードを書き続け、LSPサーバーがコードに関する情報を提供するプロセスに至ります。物事を試すためにREPLで遊ぶ代わりに、実験を単体テストとして書きます。変更(テストまたはアプリケーションロジック)を保存すると、ghcidはコードをリロードし、テストを実行し、重要な状態を失わない方法でアプリケーションを再起動します。私たちはエディタを離れることはありません。それは紙の上ではクールですが、実際にはどのように機能するか確信がなかったので、トイプロジェクトで試してみました。 トイプロジェクト この記事を書いている時点で、私は微分方程式と非線形力学に関するスキルの欠如を補おうとしていました。私が読んでいた本³³Differential Equations; Blanchard, Devaney, Hall; Cengage Learning; 2011は、計算方法に焦点を当てており⁴⁴従来の微分方程式の最初のクラス(何年も前に私が受けたものを含む)は、必要性から分析に重きを置いていました。業界では、多くの場合、微積分だけでは解決できないため、微分方程式は計算的に解決されます。そして、その本には演習を実行するためのソフトウェアが付属していますが、私は自分でコードを書きたいと思っています。少なくとも最初の数章では、ソフトウェアはロケット手術ではありません。 以下は、私のプログラムが微分方程式の一般的な解を視覚化するために明るい灰色のスロープフィールドを生成し、次にカーソルの下の座標を通る特定の解を黒で生成する例です。本からアイデアを探求するとき、私はコードを書き、テストを書き、そして保存すると、視覚化を含むウィンドウが最新の変更を反映するように自動的に更新されるようにしたいです。それは正確にはLisp開発フローではありませんが、その良い部分(特に上記で言及されたテクノロジーで可能な部分)を含んでいます。表面上はそこに到達するのは簡単そうに聞こえましたが、そうではありませんでした。 Emacsをhlsと通信させる方法 まず、Eglotとhlsを起動して実行することに焦点を当てます。これは、あまり興味のない部分です。新しいプロジェクトでゼロからインストールするには多くの手順が必要なため、以下のリストには多くの手順が含まれています。この最初のインストール後、プロジェクトでhlsを有効にするには、手順2と4のみが必要です。 最初にcabal initを実行して、実行可能ファイル、ライブラリ、およびテストコードに分割された標準的な空のHaskellプロジェクトを作成しました。後で、ghci(したがってghcidも)がそのようなマルチパートプロジェクトのリロードに関してうまく動作しないことを発見しました。したがって、すべてのコードを1つの実行可能プロジェクトに共存させることになりました。開発が他のタイプで難しくなるため、本番コードではそうしたくありませんが、この種のトイプロジェクトではうまくいっています。 flake.nixを作成してcabalプロジェクトをラップし、ライブラリやビルドツールを含む依存関係管理を提供しました。これにより、ghcidとhlsをグローバルにインストールする必要がなく、このプロジェクトにサンドボックス化できました。Nixは、新しいことを恐れずに試すのに優れています。 direnvをシステムパッケージマネージャーでインストールし、シェルのユーザープロファイルにフックしました。⁵⁵実際、すでにインストールしていました。