ClojureScript

カスタム REPL

このページでは、cljs.repl で提供される機能を使用するカスタム REPL の要件に関する最近の変更について説明します。これらの変更は、すべての ClojureScript REPL の起動時間を大幅に短縮し、コンパイルされたソースとの REPL 状態の同期を簡素化するという目標に向けて行われました。これは、グローバルに利用可能なコンパイルキャッシュインフラストラクチャを再利用することで実現されます。実際、現在では、:output-dir を既存のコンパイルキャッシュに設定して REPL を起動し、分析やコンパイルを発生させないことが可能です。

新しいインフラストラクチャでは、すべての組み込み REPL は最新のハードウェアで 1 秒未満で起動できます。

期待されること

REPL を可能な限り迅速に起動するために、REPL は、典型的なコンパイラビルドオプションを取る新しい2引数arityの -setup を実装する必要があります。以前は、-setup は非同期にすることが許可されていましたが、これはもはやサポートされていません。REPL は、-setup 中に cljs.core とそのすべての依存関係をコンパイルしてロードする必要があります。-setup では、REPL はビルドオプションを使用して、コンパイルされた JavaScript と分析情報を期待される場所にキャッシュする必要があります。ユーザーが入力したコンパイル済みフォームをストリーミングすることは問題ありませんが、名前空間のロードには絶対に避けるべきです。REPL は、ターゲット環境が goog.require を解釈することに依存する必要があります。これには、正確なソースマッピング情報など、多くの利点があります。

新しい Node.js REPL は、新しいパターンの良い例です。Node.js REPL は、Node.js ランタイム自体が goog.require を解釈することに依存しているため、短いものとなっています。

cljs.repl/load-file および cljs.repl/load-namespace を調べると、新しいアプローチが明確になります。

  • 名前空間がコンパイルされていることを確認します。

  • ファイルに対する goog.addDependency 文字列を計算し、それを評価します。

  • 名前空間の goog.require ステートメントを発行し、それを評価します。

REPL は、カスタムの goog.require の動作を得るために、グローバルな CLOSURE_IMPORT_SCRIPT 関数をオーバーライドする必要があります。

ロード済みライブラリのトラッキングの排除

新しい変更の下では、REPL は Clojure 実装内で直接ロードされたライブラリを明示的に追跡することを気にしなくてもよくなりました。代わりに、REPL は、JavaScript 評価環境が cljs.core/__loaded-libs__ を尊重するように手配する必要があります。必要に応じて、CLOSURE_IMPORT_SCRIPT に必要なロジックを埋め込む必要があります。

履歴: これは以前は、名前空間がすでにロードされている場合、goog.provide がスローされたためだけに行われていました。これは、「初心者」に教えることを目的とした完全に誤ったエラーです。goog.isProvided_ を常に false を返す関数になるようにモンキーパッチを当てることで、エラーを抑制できます。繰り返しになりますが、Node.js REPL は、このようなパッチと、CLOSURE_IMPORT_SCRIPT 実装で __loaded-libs__ を尊重する良い例です。

特殊関数

すべての REPL は、いくつかの「特殊関数」をサポートしています。特殊関数は、REPL 環境、分析環境、フォーム、および(オプションで)コンパイラビルドオプションを取る必要があります。そのままでは、in-nsrequireload-file、および load-namespace が提供されます。

出力

カスタム REPL は、printlnprint、または flush を直接呼び出すべきではありません。代わりに、-setup に渡される opts (2 番目の引数) に関連付けられた :print:print-no-newline、および :flush の値を尊重する必要があります。また、:print および :print-no-newline に関連付けられた関数は、正確に 1 つの引数を受け取ることに注意してください。

ソースマッピング

すべての REPL は、ソースマッピングのサポートを「無料」で得るために、新しいプロトコルを実装できるようになりました。評価からの :exception 結果の場合、REPL インフラストラクチャは、REPL 評価環境が cljs.repl/IParseStacktrace を満たしている場合に -parse-stacktrace を呼び出します。REPL 評価環境は、元の JavaScript スタックトレース文字列、元のエラー値全体、および REPL に渡されたすべてのビルドオプションを受け取ります。その後、REPL 評価環境は、次の形式をとる必要がある標準のスタックトレースを返す場合があります。

[{:function <string>
  :file <string>
  :line <integer>
  :column <integer>}*]

:file は、:output-dir を基準とした URI プロトコルなしの URL スタイルのパス(スラッシュ)である必要があります。

このコミットにより、REPL定義の関数に対応するために、契約がわずかに緩和されました。:file 値は、ソースが存在しないことを示すために < で始まる可能性があり、トレースに "NO_SOURCE_FILE" が出力されます。

カスタム REPL は、スタックトレースの印刷をさらにカスタマイズまたは制御したい場合があります。フックが提供されており、REPL 評価環境は cljs.repl/IPrintStacktrace を実装できます。-print-stacktrace は、マッピングされた標準スタックトレース、元のエラー値全体、および REPL に渡されたすべてのビルドオプションを受け取ります。

オリジナル作者: David Nolen