Clojure

よくある質問

これらの質問と回答は、メーリングリストやその他のClojureコミュニティフォーラムから抜粋したものです。

リーダーと構文

キーワードはキャッシュされ、インターニングされます。つまり、キーワードはプログラム全体で再利用され(メモリの削減)、等価性のチェックは実際には同一性のチェック(高速)になります。さらに、キーワードはマップ内で自身を検索するために呼び出すことができ、そのため、マップのコレクションから特定のフィールドを抽出するという一般的なパターンが可能になります。

リーダーのページでは、キーワードは「シンボルのようなもの」と定義され、シンボルは「数値以外の文字で始まる」ため、元の意図では:1は無効でした。実際、これが読み取れる唯一の理由は、キーワード正規表現のバグによるものです。このバグは1.6アルファ版で修正されましたが、すぐにこれらのキーワードが多くの実際のプロジェクトで使用されていることがわかりました。既存の動作中のコードを壊さないために、変更はロールバックされ、この形式は引き続きサポートされます。(コードやドキュメントでこれを明確にするための未解決の問題がいくつかあります。)

数値で始まる名前を持つ名前空間キーワードは、:foo/1のように、これまで読み取り可能または有効ではありませんでした。ただし、::1のような自動解決されたキーワードは読み取ることができますが、printからreadへのラウンドトリップはできません。

一般的に、狭く制御された範囲でない限り、数値で始まるキーワードの使用は避けるのが最善です。

keyword関数は、ユーザーデータまたはその他の入力ソースに基づいて、キーワードをプログラムで作成するために使用できます。同様に、namespace関数とname関数は、キーワードをコンポーネントに分割するために使用できます。プログラムでこの機能を使用して、識別子またはマップキーとして使用するキーワードを作成し、そのデータを印刷して読み取ることは決してありません。

このユースケース(および一般的なパフォーマンスのため)、keyword(またはsymbol)への入力には検証チェックは実行されません。そのため、印刷するとキーワードとして読み取れないキーワードを作成できます(スペースやその他の許可されていない文字のため)。これが重要な場合は、キーワードを作成する前に、まずキーワード入力を検証する必要があります。

リーダーはテキスト(Clojureソース)を受け取り、Clojureデータを返します。これはその後、コンパイルおよび評価されます。リーダーマクロは、典型的なs式ではないものをどのように読み取るべきかをClojureリーダーに指示します(例:クォート'や匿名関数#()など)。リーダーマクロを使用して、リーダーによって読み取られるまったく新しい構文(たとえば、JSON、XML、またはその他の形式)を定義できます。これは、通常のマクロ(コンパイル時に実行される)よりも強力な構文機能です。

しかし、Lispとは異なり、Clojureではユーザーがこのリーダーマクロのセットを拡張することはできません。これにより、他のユーザーが(適切なリーダーマクロを持っていないため)読み取れないコードを作成する可能性がなくなります。Clojureは、タグ付きリテラルを使用して、リーダーマクロの機能の一部を返します。これにより、拡張可能な、一般的に読み取れるデータを作成できます。

また、このことについてはClojureの歴史の論文のセクションを参照してください(「リーダーマクロ」を検索してください)。

Clojureでは、_はシンボルとして特別な意味を持ちません。ただし、_(または先頭に_)を使用して、式で使用されないバインディングを示すのが慣例です。シーケンシャルデストラクチャリングで不要な値をスキップする場合によくあります。

(defn get-y [point]
  (let [[_ y] point]   ;; x-value of point is unused, so mark it with _
    y))

?!は、Clojureの関数名の一部として特別な意味を持ちません。ただし、末尾の?を使用して述語関数を示し、末尾の!を使用して副作用のある関数を示すのが慣例です。より具体的には、末尾の?は、述語が厳密にブール値の結果(trueまたはfalse)を返すことを示します。末尾の!は元々、refトランザクション(ソフトウェアトランザクショナルメモリの意味で)内で使用すると安全ではない副作用を持つ関数を示すことを意図していました。実際には、!の使用はそれほど一貫しておらず、広義に副作用のある動作を示すために使用されることもあります。

#()は常に、指定した式の周りに括弧を含めて展開するため、この場合は(fn [x] ([x]))になります。これは、ベクトルが呼び出されると失敗します。代わりに、ベクトル関数#(vector %)またはvectorを使用してください。これは説明されている関数です。

コレクション、シーケンス、およびトランスデューサ

conj(結合)を含むほとんどのClojureデータ構造操作は、ユーザーにパフォーマンスの期待値を与えるように設計されています。conjでは、挿入は効率的な場所で行われるという期待があります。リスト(リンクリストとして)は、先頭でのみ一定時間挿入できます。ベクトル(インデックス付き)は、最後に拡張するように設計されています。ユーザーとして、どのデータ構造を使用するかを選択する際に、これを考慮する必要があります。Clojureでは、ベクトルの方がはるかに頻繁に使用されます。

特定の目標が「コレクションの先頭に追加する」ことである場合は、使用する適切な関数はconsです。これは常に先頭に追加します。ただし、これにより、元のデータ構造のインスタンスではなく、シーケンスが生成されることに注意してください。

一般的に、Clojureのコア関数を次の2つのカテゴリに分類する必要があります。

  • データ構造関数 - データ構造を受け取り、そのデータ構造の変更されたバージョンを返します(conj、disj、assoc、dissocなど)。これらの関数は常にデータ構造を先頭に取ります。

  • シーケンス関数 - 「シーケンス可能」なものを受け取り、シーケンス可能なものを返します。[一般的に、戻り値が実際にはISeqのインスタンスであることにコミットすることは避けています。これにより、場合によってはパフォーマンスの最適化が可能です。]例としては、map、filter、removeなどがあります。これらの関数はすべて、シーケンス可能なものを最後に取ります。

後者を使用しているが、前者のセマンティクスを期待しているようです(Clojure初心者にとってよくある問題です!)。シーケンス関数を適用して出力データ構造をより細かく制御したい場合は、いくつかの方法があります。

  1. mapvやfiltervなどのデータ構造相当を使用する - これは非常に限られたセットであり、これらの操作を実行してシーケンス可能ではなくデータ構造を返すことができます。(mapv inc (filterv odd? [1 2 3]))

  2. intoを使用してシーケンストランスフォーメーションの結果をデータ構造に戻す:(into [] (map inc (filter odd? [1 2 3])))

  3. トランスデューサ(おそらくintoと共に)を使用する - これは#2とほぼ同じ効果がありますが、シーケンスを作成せずに、より効率的に変換の組み合わせを適用できます。最終的な結果のみが構築されます:(into [] (comp (filter odd?) (map inc)) [1 2 3])。より大きなシーケンスまたはより多くの変換を扱う場合、これはパフォーマンスに大きな違いをもたらします。

これらはすべてeager変換であることに注意してください。これらは呼び出されると出力ベクトルを生成します。元のシーケンスバージョン(map inc (filter odd? [1 2 3]))は遅延評価であり、必要に応じてのみ値を生成します(より高いパフォーマンスのために内部的にチャンク化が行われます)。どちらが良いとか悪いとかではなく、状況に応じて使い分けが重要です。

主要なコレクションオペランドが最初にきます。そうすることで->などとその仲間を記述でき、可変長引数パラメータを持つ場合と持たない場合で位置が変わりません。これはOO言語とCommon Lisp(slot-valuearefelt)の伝統です。

シーケンスを考えると、左から読み込まれ、右から供給されると考えることができます。

<- [1 2 3 4]

シーケンス関数のほとんどは、シーケンスを消費し、シーケンスを生成します。そのため、それをチェーンとして視覚化することができます。

map <- filter <- [1 2 3 4]

そして、多くのseq関数は何らかの方法でパラメータ化されていると考えることができます。

(map f) <- (filter pred) <- [1 2 3 4]

つまり、シーケンス関数はソースを最後に取り、他のパラメータをその前に取り、`partial`は上記のように直接パラメータ化を可能にします。これは関数型言語とLispの伝統です。

これは、主要なオペランドを最後に取るという意味ではありません。シーケンス関数の中には、複数のソースを持つものもあります(concat、interleave)。シーケンス関数が可変長引数の場合は、通常、そのソースにあります。

Rich Hickeyによるコメントを参考に改変しました。

一連の変換を実行する場合、シーケンスは各変換の間に中間(キャッシュされた)シーケンスを作成します。トランスデューサは、入力に対する単一の複合変換を作成し、それを一度にeagerに実行します。これらは異なるモデルであり、どちらも有用です。

トランスデューサのパフォーマンス上の利点

  • ソースコレクションの反復 - 削減可能な入力(コレクションなど)で使用する場合、不要な入力コレクションシーケンスの作成を回避します - メモリと時間の節約に役立ちます。

  • 中間シーケンスとキャッシュされた値 - 変換は単一パスで行われるため、すべての中間シーケンスとキャッシュされた値の作成がなくなります - 再び、メモリと時間の節約に役立ちます。前の項目とこの項目を組み合わせると、入力コレクションのサイズや変換の数が増えるにつれて大きな効果が現れ始めます(しかし、どちらの数も少ない場合は、チャンク化されたシーケンスは驚くほど高速で競合します)。

トランスデューサの設計/使用上の利点

  • 変換の合成 - 変換の合成と変換の適用を分離する方が設計が明確になるユースケースがあります。トランスデューサはこれをサポートします。

  • eagerな処理 - eagerに変換を処理し(そして潜在的なエラーに遭遇する)、遅延評価よりも重要である場合に、トランスデューサは最適です。

  • リソース制御 - 入力コレクションがいつトラバースされるかをより詳細に制御できるため、処理が完了した時点も把握できます。そのため、いつ発生するかがわかっているので、入力リソースの解放やクリーンアップが容易になります。

シーケンスのパフォーマンス上の利点

  • 遅延評価 - 出力のいくつかしか必要ない場合(たとえば、ユーザーが使用する数を決定している場合)、遅延シーケンスは処理を遅らせることで、より効率的になることがよくあります。特に、シーケンスは中間結果を遅延評価できますが、トランスデューサは、すべての中間値をeagerに生成するプルモデルを使用します。

  • 無限ストリーム - トランスデューサは通常eagerに消費されるため、無限の値のストリームとはうまく合いません。

シーケンスの設計上の利点

  • コンシューマ制御 - APIからseqを返すことで、入力と変換を組み合わせて、コンシューマに制御を与えることができます。トランスデューサはこれにはあまり適していませんが(ただし、入力と変換が分離されている場合はより適しています)。

コア関数

かつては、メタデータを使用するのが現在よりも面倒でした(プライベートなdefnの構文は#^{:private true}でした)。そして、defn-は「簡単な」バージョンとして作成する価値があるように見えました。メタデータのサポートが向上し、「スタック可能」になることで、独立したメタデータの合成が容易になりました。すべてのdefフォームのプライベートなバリアントを作成するのではなく、必要に応じて^:privateメタデータを使用することが好まれます。

partial(またはcompjuxtなどの他の高階関数コンビネータ)を使用する場合、参照されるvarはpartialが呼び出される前に関数オブジェクトに評価されるため、参照される関数varの値をキャプチャします。var自体ではありません。たとえば:(partial my-fn 100)my-fn#'my-fnの現在の関数値に評価してから、それを用いてpartialを呼び出します。my-fn varがREPLで再バインドされた場合、前のpartial関数はそれらの変更を「認識」しません。関数を保持しているだけで、varは保持していないからです。

インタラクティブな開発でこれが問題になっている場合は、間接参照のレイヤーを挿入できます。1つのオプションは、代わりにvar参照#'myfnを使用することです。または、varの逆参照を再含めるために、別のfnまたはdefnを使用することもできます。あるいは、partialの代わりにfnまたは無名関数リテラルを使用することもできます。

一般的に、これは実行中のアプリでは問題になりません(varは通常再バインドされないため)が、インタラクティブなREPL開発では発生する可能性があります。

Spec

specは、APIがまだ変更される可能性があることを示すためにアルファ版です。specはClojureコアから分離されたため、specをメインのClojureバージョンとは別に更新できます。いつかspecのAPIは安定したものとみなされ、その時点でアルファ版は削除されます。specの次のバージョンはalpha.specで開発されています。

この質問に対する唯一の正解はありません。データspecの場合、独自のネームスペースに配置することが多く、データspecで使用される修飾子と一致するとは限りません。修飾子をネームスペースと一致させることで、spec内と他のネームスペースのエイリアスで自動解決されるキーワードを使用できますが、それらを結び付けるため、リファクタリングが複雑になります。

関数specの場合、ほとんどの人は、適用する関数の直前または直後に配置するか、必要に応じて要求できる(テストまたは検証用)別のネームスペースに配置します。後者の場合、Clojureコアはfoo.barの関数の関数specを保持するためにfoo.bar.specsのパターンに従っています。

正規表現演算子(cat、alt、*、+、?など)は、常にシーケンシャルコレクション内の要素を記述します。それ自体はspecではありません。specコンテキストで使用されると、specに変換されます。ネストされた正規表現演算子は、同じシーケンシャルコレクションに対する単一の正規表現specを形成するために結合されます。

ネストされたコレクションを検証するには、s/specを使用して内部の正規表現をラップし、正規表現演算子の間にspecの境界を強制します。

instrumentは、関数がその引数のspecに従って呼び出されているかどうかを確認することを目的としています。つまり、関数は正しく呼び出されていますか?この機能は開発中に使用する必要があります。

関数が正しく動作するかどうかを確認することは、テスト時のアクティビティであり、これはcheck関数を使用して確認する必要があります。この関数は、生成された引数で関数を実際に呼び出し、各呼び出しでretとfnのspecを検証します。

はい、Javaシステムプロパティ-Dclojure.spec.skip-macros=trueを設定すると、マクロ展開中にマクロspecはチェックされません。

Specの一般的な哲学は、「オープン」specであり、マップは、s/keys specで必須またはオプションとして指定されているもの以外にも追加のキーを含めることができます。制約されたキーセットを実現する1つの方法は、追加の制約をs/andすることです。

(s/def ::auth
  (s/and
    (s/keys :req [::user ::password])
    #(every? #{::user ::password} (keys %))))

現在、できません。これはspecの次のバージョンで検討中です。

状態と並行性

これらはそれぞれ異なるユースケースに対応しています。

  • reducerは、既存のメモリ内データ(マップやベクトル内)に対する変換を計算する場合の、きめ細かいデータ並列処理に最適です。一般的に、数千個の小さなデータ項目を計算し、多くのコアで作業を行う場合に最適です。「容易に並列化できる」と記述されているものすべてです。

  • futuresは、作業をバックグラウンドスレッドにプッシュし、後で取得する場合(または並列でI/O待ちを行う場合)に最適です。大きなまとまったタスク(バックグラウンドで大量のデータを取得する)に適しています。

  • core.asyncは主に、アプリケーションのサブシステムまたは内部構造を整理するために使用されます。値を1つの「サブプロセス」(goブロック)から別の「サブプロセス」に伝えるチャネル(キュー)があります。そのため、プログラムをどのように分割するかによって、並行性とアーキテクチャ上の利点が得られます。core.asyncでしか得られない優れた機能は、複数のチャネルからのI/Oイベントを、どれか1つでも最初の応答を待つ機能です(alt/altsを使用)。Promiseも独立したスレッド/サブプロセス間で単一の値を伝えるために使用できますが、単一配信のみです。

  • pmap、java.utilキューとexecutor、claypooleのようなライブラリは、粗いレベルの「タスク」並行処理を行っています。非常に便利なトランスデューサフレンドリーなパイプライン機能を持つcore.asyncとここで重複があります。

これは、futurepmapagent-send、またはそれらの関数を呼び出す他の関数を使用するプログラムのコンテキストで最もよく質問されます。このようなプログラムが終了すると、終了する前に60秒間のポーズがあります。この問題を解決するには、プログラムの終了時にshutdown-agentsを呼び出します。

Clojureは、futuresとagentの関数実行をサービスするために、2つの内部スレッドプールを使用します。両方のプールはデーモンではないスレッドを使用し、デーモンではないスレッドがアクティブな間はJVMは終了しません。特に、futuresとagentの送信呼び出しをサービスするプールは、60秒のタイムアウトを持つExecutorキャッシュスレッドプールを使用します。上記のシナリオでは、プログラムはバックグラウンドスレッドが作業を完了し、スレッドが期限切れになるまで待機してから終了します。

デフォルトで読み込みが含まれている場合、STM は遅くなります(より多くのトランザクションが直列化を必要とするため)。しかし、多くの場合、読み込みを含める必要はありません。そのため、ユーザーは必要に応じてパフォーマンスの低下を受け入れ、そうでない場合はより高速なパフォーマンスを得ることを選択できます。

名前空間

いいえ(ただし、それが一般的です)。1つの名前空間は、`load`を使用してセカンダリファイルを読み込み、それらのファイルで`in-ns`を使用して名前空間を保持することにより、複数のファイルに分割できます(clojure.coreはこのように定義されています)。また、1つのファイルに複数の名前空間を宣言することも可能です(ただし、これは非常にまれです)。

nsは、いくつかの処理を行うマクロです。

  • 新しい内部名前空間オブジェクトを作成します(まだ存在しない場合)。

  • その名前空間を新しいカレント名前空間(`*ns*`)にします。

  • clojure.coreからのすべてのvarを自動参照し、java.langからのすべてのクラスをインポートします。

  • 指定された他の名前空間とvarをrequire/referします。

  • (およびその他のオプションの項目)

nsは、あなたが示唆するように、関数または呼び出し可能なものを返しません。

nsは通常cljファイルの先頭に配置されますが、実際には通常のマクロであり、REPLでも同様に呼び出すことができます。1つのファイルで複数回使用することもできます(ただし、これはほとんどのcljプログラマーにとって驚くべきことであり、AOTでは期待通りに動作しない可能性があります)。

コンパイラ

直接リンクされたものは、varへの再定義を認識しません。たとえば、clojure.coreで何かを再定義した場合、そのvarを使用するcoreの他の部分は再定義を認識しません(ただし、REPLで新しくコンパイルしたものは認識します)。実際には、これは通常問題ではありません。

独自のアプリの一部については、REPLで開発するのではなく、本番環境向けにビルドしてデプロイする場合にのみ直接リンクを有効にすることをお勧めします。または、常に再定義を許可する場合には`^:redef`、動的なvarの場合は`^:dynamic`を使用して、アプリの一部をマークする必要がある場合があります。

Javaと相互運用性

外部クラスと内部クラスの名前を`$`で区切ります。たとえば、`java.util.Map$Entry`は、Map内のEntry内部クラスです。

プリミティブ型は、ボックス化されたクラスの静的TYPEフィールドとして見つけることができます。たとえば、`Integer/TYPE`です。

Javaは、末尾の可変長引数パラメータを配列として扱い、明示的な配列を渡すことでClojureから呼び出すことができます。

;; Invoke static Arrays.asList(T... a)
(java.util.Arrays/asList (object-array [0 1 2]))

;; Invoke static String.format(String format, Object... args)
(String/format "%s %s, %s" (object-array ["March" 1 2016]))

;; For a primitive vararg, use the appropriate primitive array constructor
;; Invoke put(int row, int col, double... data)
(.put o 1 1 (double-array [2.0]))

;; Passing at least an empty array is required if there are no varargs
(.put o 1 1 (double-array []))

;; into-array can be used to create an empty typed array
;; Invoke getMethod(String name, Class... parameterTypes) on a Class instance
(.getMethod String "getBytes" (into-array Class []))

Java 9ではモジュールシステムが追加され、コードをモジュールに分割できるようになりました。モジュール外のコードは、モジュール内でエクスポートされていない限り、モジュール内のコードを呼び出すことができません。Javaでのこの変更の影響を受けた領域の1つは、リフレクションアクセスです。Clojureは、ターゲットオブジェクトまたは関数引数に関する十分な型情報がないJava相互運用呼び出しが発生した場合にリフレクションを使用します。例えば

(def fac (javax.xml.stream.XMLInputFactory/newInstance))
(.createXMLStreamReader fac (java.io.StringReader. ""))

ここでは、`fac`は`com.sun.xml.internal.stream.XMLInputFactoryImpl`のインスタンスであり、`javax.xml.stream.XMLInputFactory`の拡張です。java.xmlモジュールでは、javax.xml.streamはエクスポートされたパッケージですが、XMLInputFactoryImplはそのパッケージのパブリック抽象クラスの内部実装です。ここの`createXMLStreamReader`の呼び出しはリフレクションになり、Reflectorはその実装クラスに基づいてメソッドの呼び出しを試みますが、これはモジュール外からはアクセスできず、以下のような結果になります。

WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by clojure.lang.Reflector (file:/.m2/repository/org/clojure/clojure/1.10.0/clojure-1.10.0.jar) to method com.sun.xml.internal.stream.XMLInputFactoryImpl.createXMLStreamReader(java.io.Reader)
WARNING: Please consider reporting this to the maintainers of clojure.lang.Reflector
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release

ここで最初に注意すべきことは、これが警告であるということです。Java 9から現在のすべてのリリースでは、呼び出しを許可し、コードは引き続き動作します。

いくつかの潜在的な回避策があります。

  • おそらく最適なのは、エクスポートされた型に型ヒントを提供して、呼び出しがリフレクションではなくなるようにすることです。

(.createXMLStreamReader ^javax.xml.stream.XMLInputFactory fac (java.io.StringReader. ""))
  • Clojure 1.10以降、`--illegal-access=deny`を使用して不正アクセスをオフにします。その後、Javaリフレクションシステムは、アクセスできないクラスを介して呼び出すことができないことを検出するためにClojureに必要なフィードバックを提供します。Clojureは代わりにパブリック呼び出しパスを見つけ、警告は発行されません。

  • JVMモジュールシステムフラグ(`--add-exports`など)を使用して、内部パッケージを強制的にエクスポートして警告を回避します。これはお勧めしません。

警告からリフレクションが発生している場所を判断するのが難しい場合は、フラグを追加すると役立つ場合があります。

--illegal-access=debug

たとえば、Clojure CLIを介して`-J`オプションを使用するか(または`deps.edn`のエイリアスで`:jvm-opts`の一部として)、

clj -J--illegal-access=debug

設計と使用方法

不変データに焦点を当てているため、一般的にデータカプセル化に高い価値は置かれていません。データは不変であるため、他の誰かが値を変更する心配はありません。同様に、Clojureデータは直接操作するように設計されているため、APIでラップするのではなく、データへの直接アクセスを提供することに大きな価値があります。

すべてのClojure varはグローバルに利用可能であるため、名前空間内の関数のカプセル化の方法もほとんどありません。ただし、varをプライベートとしてマークする機能(関数には`defn-`、値には`^:private`付きの`def`を使用)は、開発者がAPIのどの部分を公開して使用すべきか、実装の一部を区別するための便宜です。

依存関係とCLI

いいえ。Clojure CLIは、a) クラスパスの構築とb) Clojureプログラムの起動に焦点を当てています。アーティファクトの作成、アーティファクトのデプロイなどは行いません(行いません)が、ツールとライブラリを通じてこれらのアクションを促進する場合があります。

tools.depsは、依存関係の解決とクラスパスの構築のためのプログラム可能なビルディングブロックを提供することを目的としています。clj/clojureはこれらをコマンドライン形式にラップし、Clojureプログラムを実行するために使用できます。これらの部分を組み合わせることで、他にも多くのことができます。

いいえ。これを行うための他のツールが現在存在するか、既存の機能の上に追加できますが、これは当初の目標の一部ではありませんでした。

理想的にはShebang行を介して自己呼び出し可能な単一ファイルのClojureスクリプトをどのように作成しますか?

追加の依存関係が不要な場合は、最初の行に`#!/usr/bin/env clojure`を配置します。`clojure`は`-main`関数を自動的に呼び出さないため、ファイルが関数を定義するだけではないようにしてください。コマンドライン引数は`*command-line-args*`で見つけることができます。

追加の依存関係が必要な場合は、Dominic Monroe提供の次の手順を試してください。`funcool/tubax`の代わりに必要な依存関係を代用してください。

#!/bin/sh

"exec" "clojure" "-Sdeps" '{:deps {funcool/tubax {:mvn/version "0.2.0"}}}' "$0" "$@"

;; Clojure code goes here.

貢献

https://clojure.dokyumento.jp/contributingを参照してください。

それは2つの理由に要約されます。

  1. 企業がClojureを採用することを妨げる可能性のある将来の法的問題からClojureを保護するため。

  2. それが有利であれば、Clojureを異なるオープンソースライセンスで再ライセンスできるようにするため。

貢献者契約への署名により、Rich Hickeyはあなたの貢献物の共同所有権を取得します。その代わりに、Rich HickeyはClojureが常にFree Software FoundationまたはOpen Source Initiativeのいずれかによって承認されたオープンソースライセンスで利用可能であることを保証します。

これは、メールアカウントが既にAdobe EchoSignアカウントに関連付けられているユーザーに固有のAdobe EchoSignの特異性です。そのような場合、EchoSignは、フォームに署名された個々の名前ではなく、既存のプロファイルの会社名を件名行で使用します。心配しないでください!これは影響しません-契約は署名され、メールに添付されています。

Rich Hickeyは、JIRAチケットに添付されたパッチを評価することを好みます。これは、貢献者を困難にするため、または法的理由のためではなく、ワークフローの優先順位によるものです。詳細については開発ページを参照してください。

リンク 2012年10月のRich Hickeyによるこのトピックに関するClojure Googleグループメッセージ。

将来のアイデア

人々はしばしばClojureの「ネイティブ」バージョン、つまりJVMに依存しないバージョンを要求します。ClojureScriptの自己ホスティングは現在の1つのパスですが、おそらくユースケースのサブセットにしか役立ちません。GraalVMプロジェクトを使用して、スタンドアロンのバイナリ実行ファイルを作成できます。Graalで生成されたネイティブイメージは非常に高速に開始されますが、フルJVMよりもパフォーマンスを最適化できる機会が少ない場合があります。

しかし、これら2つのいずれも、人々が「Clojureのネイティブバージョン」を求める際に想像しているものではない可能性があります。それは、JVMでホストされず、おそらくLLVMのようなものでネイティブ実行ファイルに直接コンパイルされる言語のバージョンです。Clojureは、JVMから膨大な量の性能、移植性、機能を活用しており、世界クラスのガベージコレクターなどのものに大きく依存しています。「Clojureネイティブ」を構築するには、Clojureのバージョンを作成するために大量の作業が必要になります。それは遅く(おそらくはるかに遅く)、移植性が低く、機能が大幅に少なくなります(ClojureライブラリはJDKに大きく依存しているため)。Clojureコアチームはこの作業を行う予定はありませんが、誰にとっても素晴らしい学習プロジェクトであり、ぜひ挑戦することをお勧めします!

原著者:Alex Miller