ClojureScript

FAQ (JavaScript開発者向け)

このページは、ClojureScriptの使用に関してJavaScript開発者が抱える可能性のある懸念点に答えることを目的としています。

言語機能とセマンティクス

実際には、不変データ構造は変更できます。ただし、「変更」の意味が異なるだけです。この場合、変更とは、最初に使っていたものとはいくつかの点で異なる新しいデータ構造を作成することを意味します。

JavaScriptでは、文字列、ブール値、および数値を使用する場合に、すでにこれを行っています。数値をインクリメントすることは新しい数値を作成することであり、文字列に追加することは新しい文字列を作成することです。

したがって、不変データ構造を制約されたコンテナとしてではなく、複合値として考える必要があります。

永続データ構造を効率的に実装するのは困難であることをご安心ください。舞台裏で重労働をしている高度なアルゴリズムがあるので、両方とも手に入れることができます。これは、ガベージコレクションに似ています。これは、コンピューターにとっては自然ではないが、人間にとってはより自然なセマンティクスを生み出すアルゴリズムの革新です。

これはすべて素晴らしいことですが、値を使用したプログラミングによって私の生活がどのように向上するのでしょうか?

最もすぐに目に見えるメリットは、防御的なコピーやクローン作成などの脆弱なテクニックを使用する必要がなくなることです。コードの他の部分がデータ構造を密かに変更することを心配する必要がなくなるからです。

具体的な例を見てみましょう。WebアプリにPersonモデルがあり、Personのフィールドの値を編集して変更を送信できるコンポーネントがあるとします。値を使用したプログラミングでは、次の機能を簡単に実現できます。

  1. ある時点でのモデルの現在のバージョンから編集を開始する

  2. 編集時、変更はアプリの他の部分に影響を与えることなく、編集コンポーネントに対してローカルにのみ影響するようにする

  3. 逆に、ローカル編集プロセスが開始時のバージョンを処理している間、アプリが外部ソース(APIからプッシュされた更新など)からのモデルへの変更を確認できるようにする

  4. 編集中のアンドゥとリドゥをサポートする

  5. 変更を送信するときは、変更がすぐに表示されるように、Webアプリのモデルの値を楽観的に変更し、サーバーがエラーを返してきた場合は元に戻す。

さらに深く言えば、値は情報システムを作成するための可変データ構造よりもはるかに自然な適合であると信じる強力な理由があります。これは、Webアプリのようなネットワーク集約型のアプリケーションで特に当てはまります(値はあなたが有線で送受信するもの。まるでAJAX呼び出しがデータベースを変化させるデータ構造を与えるかのようです)。値について優れた主張をしている講演があります。

最後に、値ベースのプログラミングのメリットを考慮しなくても、ClojureScriptのコレクションは非常に完全な標準ライブラリのおかげで、非常に強力で使いやすいことがわかります。

データ構造は不変であり、ローカルは可変ではないため、時間の経過とともに進化するプログラムをどのように作成できますか?

ご心配なく。モナドを使用する必要はありません。ClojureScriptは可変状態の必要性を認識し、それを管理するための参照型であるatomを提供します。以下に例を示します

JavaScript

// declaring the state
var state = {
  count: 0
};

// ...

// updating the state :

state.count = state.count + 1;

ClojureScript

;; declaring the state
(def state (atom {:count 0}))

;; ...

;; updating the state
(swap! state #(update % :count inc))

ClojureScriptのatomは、JavaScript関数が、たとえばJavaのメソッドよりも優れているのと同じように、JavaScript変数よりも優れています。つまり、ファーストクラスであるということです。関数に渡したり、データ構造で参照したり、抽象化をそれらの上に構築したりできます。さらに、atomは観測可能です。

JavaScriptの変数よりも、ClojureScriptのatomを使用する数がはるかに少ないことがわかります。atomと不変データ構造を組み合わせることで、プログラム全体に散らばっているのではなく、いくつかの主要な場所で状態を管理できます。

多くの人が、マクロをコンパイルの最初のステップをカスタマイズする簡単な方法として定義しています。これは、マクロを使用した後に初めて理解できる種類の定義であるため、別の方法で説明しましょう。

マクロを使用すると、コードの変換をいくつか指定できます。my-macroというマクロを使用することは、ClojureScriptに「(my-macro <この見栄えの良いコード>)と書いたときは、`(<このより面倒なコード>)’を意味する」と言うようなものです。関数がプログラムの実行の一部を因数分解するのと同じように、マクロはコードの記述の一部を因数分解します。

その結果、マクロを使用すると、構文が邪魔になることはないため、常にコードの呼び出しを可能な限り快適にすることができます。

たとえば、dotoマクロを使用すると、次のように書くことができます

(def my-date (doto (new Date)
               (.setDate 7) (.setMonth 7) (.setFullYear 1991)))

これは、あたかも次のように書いたかのようです

(def my-date
  (let [d (new Date)]
    (.setDate d 7)
    (.setMonth d 7)
    (.setFullYear d 1991)
    d))

これは、JavaScriptで(苦労して)書いたはずのものです

var myDate = (function(){
  var d = new Date();
  d.setDate(7);
  d.setMonth(7);
  d.setFullYear(1991);
  return d;
}());

より深く言えば、マクロを使用すると、適切に構造化されたプログラムを作成することと、構文の観点からそれらを実用的にすることという2つの重要な懸念事項を分離することが簡単になります。

しかし、マクロは単にボイラープレートを排除する手段ではありません。ClojureScriptの構文を拡張および操作できるようにすることで、プログラムに新しいパラダイムをインポートできます。

ClojureScriptのマクロを使用すると、これらの「機能」をClojureScriptにライブラリとしてアラカルトで追加できます。

マクロはJavaScriptを生成するときにすべて実行されるため、ランタイムオーバーヘッドがないことにも注意してください。

実際、ClojureScriptコミュニティでさえ、マクロを使用する必要がない場合は使用しないことが悪いスタイルと見なされています。ClojureScriptアプリケーションの開発者は、ほとんどの場合、関数でもジョブを実行でき、推論が容易であるため、マクロを記述することはめったにありません。

しかし、時々、関数で構文の面倒さを軽減できない場合や、コード分析が必要な場合があるため、マクロだけが達成できる概念的な飛躍を行う必要があります。

マクロは飛行機のようなものです。毎日仕事に行くのに飛行機に乗りたいとは思わないでしょう。しかし、時々、飛行機を使用すると数時間で別の大陸に到達できるため、それらがあってよかったと思います。

実際、JavaScriptの構文からClojureの構文に移行するのは非常に困難です

JavaScript

myFun(x, y, z);

ClojureScript

(myFun x y z)

演算子の片側からもう片側に1つの括弧を移動し、コンマとセミコロンを削除する必要があります。

Clojureの構文(別名EDN - 拡張可能データ表記法)は、Clojureでマクロを実用的に記述できる理由です。

これはあなたには馴染みがないかもしれませんが、不自然ではありません。慣れると、JavaScriptよりも規則性が高く、混乱が少ないことがわかります。

これは楽しい。データ構造リテラルでこれをもう一度やりましょう。

JavaScript

{a : "b",
 c : [d, e]}

ClojureScript

{:a "b"
 :c [d e]}

ご覧のとおり、主な違いはコンマとコロンがなくなることです。

興味深いことに、それほど多くのようには見えませんが、これはWebプログラミングの重要な部分であるHTMLテンプレートに大きな影響を与えます。

Clojureのデータ表記法は非常に便利で軽量であるため、いくつかのClojureライブラリでは、それを使用してHTMLテンプレートを言語に埋め込んでいます。

[:div.text-right
  [:span "Click here: "]
  [:button {:class "btn" :on-click #(do something)} "Click me!"]]

Clojurescriptコードに必要な括弧の数に悩まされている人もいます。慣れると、Clojureのインデント規則は問題ないことがわかります。言うまでもなく、括弧、中括弧、および角括弧を一致させるのに役立つエディターを使用する必要があります。コードのフォーマットに役立つエディターも使用すると、括弧で間違いを犯したかどうかをすぐに確認できます。

ClojureScriptには、非常に優れたJavaScript相互運用性があります。ClojureScript関数は、通常のJavaScript関数です。この言語は、ネイティブブラウザーオブジェクトにアクセスしたり、オブジェクトのプロパティにアクセスして設定したり、JavaScriptオブジェクトと配列を作成および操作したり、任意のJavaScript関数またはメソッドを呼び出したりするためのプリミティブを提供します。

ClojureScriptで関数を記述して、JavaScriptから呼び出すこともできます。

はい。たとえば、多くのClojureScript開発者は、Reactやd3などのライブラリを使用しています。

ES2015、ES7などは、JavaScriptに多くの表現力(アロー、ジェネレーター、分割代入など)をもたらし、いくつかの欠点と欠陥(モジュール、ブロックスコープなど)に対処しています。これにより、ClojureScriptのような言語は無意味になるのではないでしょうか?

機能をリストしてプログラミング言語を比較するのは危険な練習ですが、とにかくやってみましょう。新しいECMAScriptバージョンがもたらす構文拡張のほとんどは、Clojureが提供しています。次の表に、最近のECMAScriptの機能とそれに対応するClojureScriptの機能を示します。

ECMAScript ClojureScript

アロー

Clojureの関数式はすでに非常に簡潔で、式ベースです((fn [x y] (+ x y))function (x, y){return x + y;}と比較してください)。さらに、さらに軽量な関数構文(#(+ %1 %2))があります。

クラス

ClojureScriptは、設計上クラスベースではありません。データ型とプロトコルを使用して、オブジェクト指向の良い部分を入手してください。詳細については、次のセクションを参照してください。

テンプレート文字列

ClojureScript文字列は複数行であり、コンマがないため、連結にstr関数を使用するのが非常に自然です。Google ClosureライブラリのCスタイルのフォーマットを使用することもできます。

分割代入

 利用可能であり、ネストと関数パラメータもサポートします。

デフォルトと残りの引数

完全にサポートされています

letとconst

 これらは、ClojureScriptのletの正確なセマンティクスです。

イテレーターとFor..Of

 すべてのデフォルトコレクションによって実装されるSeq抽象化。

ジェネレーター

遅延シーケンス

セットとマップ

標準ライブラリの一部(永続的なデータ構造として)で、任意のキーを使用できます。

プロキシ

関係なし

シンボル

 設計上、ClojureScriptは情報の隠蔽に反対しているため、プライベートメンバーはありません。キーワードは名前空間化できるため、マップキーの競合の可能性が減少します。プロトコルを使用すると、新しい表示可能なメンバーなしで、既存のデータ型の動作を拡張できます。

Math + Number + String + Object API

 標準ライブラリとGoogle Closureライブラリの両方で同様の機能。

数値リテラル

8進数と16進数(そしてもちろん10進数)の特別な構文を使用して、任意のベースのリテラルがあります

Promise

ライブラリ経由で利用可能

リフレクトAPI

関係なし

末尾呼び出し

明示的なrecurコンストラクトを介して部分的にサポートされます。

Clojureは、JavaやRubyのようなクラスベースの言語に具現化されたオブジェクト指向の限界への答えとして、少なからず誕生しました。

Clojureの観点からすると、クラスはデータの表現、プログラムロジック、コード構成、状態管理、そしてポリモーフィズムを混同しています。これらの懸念事項はすべて、Clojureではデータ構造、関数、名前空間、管理された参照、および「アラカルトのポリモーフィズム」構造(プロトコルとマルチメソッド)によって個別に対処されます。

しかし、継承がない場合、どのようにコードを再利用するのですか?

オブジェクト指向の世界でさえ、専門家はコードの再利用を実現するために継承よりもコンポジションを優先するように言うでしょう。関数は非常に粒度が細かいため、構成が非常に簡単です。

データ構造もクラスよりも再利用が容易です。なぜなら、特定性が少ないからです。

(これらの利点はどちらもClojure固有のものではありません。すでにJavaScriptを関数型でデータ指向の方法で使用することで経験しているかもしれません。)

オブジェクト指向のバックグラウンドが強い場合、クラスなしで生活することを学ぶのに時間がかかるかもしれません。心配しないでください。この学習曲線は下降し、あなたの努力は報われるでしょう。

エコシステム

それはClojureScript自体です!ClojureScriptには優れたコレクションライブラリが付属しています。あなたが知っていて愛しているすべてのコレクション関数(map、reduce、filter、remove、…)があり、それらは抽象化された上で動作します。つまり、JavaScriptのオブジェクトや配列のようなものに限定されません。

ClojureScriptにはAngularJsやBackboneに相当するものはありません。これは実際には良い兆候です。これらのクライアントサイドフレームワークの動機となったのは、JavaScriptのモジュール性、プリミティブ、およびまともな標準ライブラリの欠如でした。

ClojureScriptはプラットフォームとしてこれらの問題に対処し、Google Closureライブラリに依存してブラウザの不整合に対処します。アプリケーション構築のその他の懸念事項(例:テンプレート、サーバー通信、ルーティングなど)は、専用ライブラリを組み合わせて対処します。

ClojureScriptプロジェクトを開始するには、ClojureScript Wikiにいくつかのプロジェクトテンプレートと、ライブラリのカタログが用意されています。

はい、います

プログラミング言語としてのClojureScriptは非常に成熟しています。Clojureは、2008年に一般公開される前に、数年間慎重に設計されました。2011年にClojureScriptが登場したとき、ClojureはJVMで数年間テストされ、実績がありました。これは、Clojureのシンプルさを重視することと、マクロが多くの難しい言語設計の決定を排除するという事実と組み合わさって、Clojureは言語としてわずか数年で安定しました。

JavaScriptが経験している変化(新しい言語機能、コミュニティにおけるパラダイムシフト、プログラミングショップでの規約の変更、およびそれらがツールとライブラリに引き起こすすべての変更)が問題である場合、ClojureScriptはあなたにとって良い場所かもしれません。

もちろん、言語がすべてではなく、ClojureScriptライブラリエコシステムは今後数年間で重要な変革を経るでしょう。しかし、ReactやFalcorのようなパラダイムシフトライブラリの大規模な採用や、アプリケーションプラットフォームと要件の変化が示しているように、これはJavaScriptエコシステムでも起こっていることに注意する必要があります。

ReactとFlux

はい、ClojureScriptコミュニティのほぼすべての人が、ClojureScriptに具現化された関数型プログラミングとの深い相乗効果のためにReactを使用しています。実際、多くのClojureScriptプログラマーは、ClojureScriptをReactを活用するための最良の方法と考えています。

豊富なコレクションライブラリ、高度な制御フロー演算子(condcasewhenletif-let、パターンマッチングなど)、およびすべてが式であるという事実は、非常に直接的で宣言的な方法でレンダリング関数を作成できるようにします(多数の中間変数を配置する必要はありません)。

Fluxは、永続的なデータ構造と管理された参照(アトム)の組み合わせのおかげで、ClojureScriptで非常に簡単に実装できます。これは、Omのような影響力のあるライブラリの存在理由の一部です。

多くの点で、ClojureScriptは、不変データ構造、「すべての状態を1か所に」という原則、およびその他の関数型手法の漸進的な採用によって示されているように、より広いReact/Fluxコミュニティをリードしています。

はい!ClojureScriptでReactを使用できるという事実に加えて、Reactに対する最も人気のあるClojureScriptラッパー(Om、Reagent、Quiescent)はすべて、Reactコンポーネントを簡単に含めることができます。

ツール

間違いなくあります。多くの人がEmacsとClojureを使用しており、IntelliJVim、Eclipse、およびSublime Text用の優れたプラグインもあります。

構造編集は、コードの構成要素(つまり、行や単語ではなく式)を自然に操作できるため、慣れているものよりも実用的であることがわかるでしょう。

はい。ほとんどのClojureScript開発者は、Leiningenを使用してClojureScriptプロジェクトを管理します。Leiningenは、依存関係のロード、パッケージングを処理し、フロントエンド開発ワークフロー(CSSプリプロセス、アセットの最小化など)用のプラグインを備えています。もう1つの人気のあるツールはBootです。

特に、Figwheelを使用したClojureScriptは、ライブコードのリロードとClojureScript REPLのおかげで、インタラクティブなフロントエンド開発の最先端と言えるでしょう。

Google Closureは、フロントエンドのJavaScript開発者にとって魅力的な利点を提供します。

  • 非常に包括的で、実証済みのライブラリ

  • 非常に効率的な最小化

  • デッドコードの削除。つまり、使用されていないコードは削除されます。(これにより、ライブラリは包括的になります。機能を追加するときに、追加のバイト数について心配する必要はありません。)

JavaScript開発者の大部分は、デッドコードの削除を機能させるために、記述するJavaScriptについて厳格な規律に従う必要があった(特に、Javaに非常によく似たものになった)という大きな欠点のために、Google Closureを拒否しました。

ClojureScriptからGoogle Closureを使用する場合、この障害はありません。ClojureScriptコンパイラーは、Closure用に最適化されたJavaScriptをすぐに生成するためです。これは、言語セマンティクスを犠牲にすることなく、上記の利点を享受できることを意味します。

デッドコードの削除は、アプリケーション開発者にとって非常に便利ですが、エコシステムの開発にとってもさらに大きなメリットがあります。ライブラリ作成者は、ユーザーが使用するバイト数のみを取得するため、機能と重さのトレードオフを行う必要がなくなりました。

プラットフォーム

まず、ClojureScriptはすべての主要なJavaScriptエンジンをターゲットにしています。したがって、NodeJSでClojureScriptを実行できます

ただし、NodeJSサーバーを記述するためのClojureScriptの例はあまり見つかりません。さまざまな理由から、人々はサーバー上でオリジナルのJVM Clojureを好む傾向があります(ライブラリエコシステムはより成熟しており、非同期コードを強制することはありません)。

Clojure 1.7以降、JavaとJavaScriptの両方のランタイムをターゲットとするClojureコードを記述することが非常に簡単になりました。

ただし、そのアプローチが失敗した場合(サーバーとクライアントの両方でJavaScript固有の機能に依存する必要がある場合)、人々はJava 8に組み込まれているJavaScriptランタイムであるNashornに頼る傾向があります。

これは私たちを次に導きます。

さまざまな 概念実証がこの目標に向けて公開されていますが、現在、この問題に対するFluxibleのような既製のライブラリソリューションはありません。

ClojureScriptはES3準拠のコードにコンパイルされます。ClojureScriptで移植可能なコードを記述するには、バニラJSよりも少ない規律が必要です。

実用

デバッグストーリーはどうですか?

ClojureScriptは、従来のJavaScriptデバッグ手法を強力にサポートしています。ソースコードにブレークポイントを設定したり、ClojureScriptのスタックトレースを取得したりできます。これは、高度な最小化を使用した本番コードでも機能する、非常にうまく設計されたソースマップのおかげです。

これに加えて、REPL駆動の開発とホットコードのリロードは、バグの条件を消去することなく、ステートフルプログラムをテストおよび変更できるようにすることで、これにまったく新しい側面をもたらします。

さらに素晴らしいこと:ここでもマクロが役立ちます。一般的なJavaScriptのデバッグ手法は、コードの途中にconsole.log呼び出しを挿入することですが、これはすぐに退屈で邪魔になります。

たとえば、このコードがあり、myFun関数に欠陥がある疑いがあると仮定します。

var result = x + myFun(y);

あなたが行う必要があるのは

var z = myFun(y);
console.log("myFun(y) : ", z);
var result = x + z;

ClojureScriptでは、非常に軽量な方法で同じことを行うspyマクロを定義できます。したがって、同等のClojureScriptコードは

(let [result (+ x (myFun y))])

次のようになります

(let [result (+ x (spy (myFun y)))])

そして、同じ情報がコンソールに表示されます。クールでしょ?

多くの人の意見では、JavaScriptよりもそうです。言語セマンティクスはあまりとらえどころがなく、ツールは心配なく使用できるほど成熟しています。

明らかに、ClojureScriptはJavaScriptの上に抽象化されたものなので、特に永続的なデータ構造のために、遅くなるはずです。

実際、ClojureScriptの作成者は、JavaScriptの機能がClojureScriptのセマンティクスの非常に自然で直接的な基盤であるという嬉しい驚きを抱いていました(たとえば、ClojureScript関数は通常のJavaScript関数です。ClojureScriptプロトコルは非常に簡単な方法でJavaScriptプロトタイプにマップされます)。結果として、ClojureScriptは非常に高速です。

ClojureScriptの永続データ構造は、明らかにJavaScriptの配列やオブジェクトよりも遅いですが、単純な方法(クローン作成やコピーオンライトではありません)で実装されていないため、思ったほど遅くはありません。アイデアを示すためのベンチマークを次に示します。

さらに、永続データ構造により、可変データ構造では不可能なグローバル最適化が可能になります。特に、ClojureScriptは、キャッシュに永続データ構造を使用することで、Reactアプリのパフォーマンスを大幅に向上させることが知られています(http://swannodette.github.io/2013/12/17/the-future-of-javascript-mvcs)。

ClojureScriptの学習

これは、人々のプログラミング経験と適性によって大きく異なります。

あなたがJavaScript開発者であれば、Clojure(Script)を学ぶのに良い出発点になります。RubyやJavaなどの従来の言語でプログラミングする場合よりもそうです。なぜなら、JavaScriptはすでに、ファーストクラス関数、動的型付け、データ構造(クラスではない)で情報を伝達し、名前空間(クラス階層ではない)でコードを整理するように教育しているからです。

概念的には、ClojureScriptで生産性を高めるためにJavaScriptよりも学ぶ必要があることは少なくなります。JavaScriptのすべての罠を避けることを学ぶ必要がないからです。REPLとドキュメントが言語に含まれているという事実は、学習プロセスをスピードアップする上で重要な役割を果たします。

おそらく、大きな課題は新しい概念を理解することではなく、古い概念を忘れることでしょう。ヨーダが言ったように、「(命令型やオブジェクト指向パラダイムについて)学んだことを忘れなければならない」のです。これは経験豊富なプログラマーにとってはより困難です。とはいえ、もしあなたが(例えば、ダグラス・クロックフォードが提唱しているように)JavaScriptで関数型に近いスタイルで開発してきたのであれば、これはそれほど問題にはならないでしょう。

関数型プログラミングは博士号を持っている人にしか理解できないと聞いたことがあります。

ClojureScriptを使うために、圏論やモナドなどを知る必要はありません。あなたがするであろう仕事の99%では、JavaScriptですでに使用しているプログラミングの概念(動的型付け、関数、データ構造)が必要になるでしょう。

コミュニティ

コミュニティは存在し、急速に成長しています。実際、Clojureについて人々が最も愛しているのは、多くの場合、コミュニティです。Slackや様々なメーリングリストは非常に活発で、助けが必要なときにはいつでも対応してくれます。

Clojure(Script)コミュニティの特徴は、実用主義、創造性、高い品質基準、そして賢明なリーダーシップの組み合わせです。

(それに、人々はとても親切です。)

Clojureの人々は、現実世界での実際の付加価値を冷静に見ることなく、単にいくつかのエレガントなアイデアに夢中になっているのではないかと疑問に思っています。

Clojureコミュニティのほとんどの人は、その作成者のアイデアに触れたとき(特に、Simple Made EasyThe Value of Valuesのような素晴らしい講演を通して)、一種の啓示を経験し、Clojureでのその具現化を採用したときに大きな報酬を感じました。結果として、私たちは時に客観的ではないように見えるほど熱狂的になることがあります。

しかし、実用性と実用主義は常にClojureの中核的な価値観であり、JVMやJavaScriptランタイムという人気のプラットフォームをターゲットとしたことや、他の言語と比較して機能的純粋性を犠牲にする設計上の決定といった、根本的な決定の動機となっていることを知っておくべきです。Clojureコミュニティが、実用的なツールを開発し、Clojureのリーチを広範なプラットフォームに拡大するために尽力してきたことからも、彼らがこの精神に忠実であり続けていることがわかるでしょう。

その他

Clojureは、シンプルさを目指すことがプログラマーを劇的に力づけることができるという根本的な信念に基づいています。直感に反する意味合いは、洗練されたツールやテクニックを取り除くことが、実際にはあなたをより効果的にするということです。それを経験するまでは信じられないでしょう。

このFAQに追加の質問をこちらで提案できます。

それまでの間、StackOverflowやメーリングリストで、コミュニティに気軽に連絡してください。人々はとてもフレンドリーです!

オリジナル著者:David Nolen