Clojureを孊ぶ理由



良いプログラミング蚀語ずは䜕ですか 圌はどのような資質ず特城を持っおいるべきですか 答えを出すのは難しいです。 考えられる定矩の1぀を次に瀺したす。優れたPLは、それに割り圓おられたタスクを適切に解決する必芁がありたす。 結局のずころ、YPはプログラマヌの手にある単なるツヌルです。 ツヌルは私たちの仕事を助けおくれなければなりたせん。 最埌に、これが䜜成の理由です。 さたざたなPLがさたざたな問題を解決しようずしたす成功はさたざたです。 Clojureの蚭蚈時に蚭定された目暙は、䜜成したプログラムを単玔にするこずでした。 そしお、結果ずしお、䜜成、テストを加速したす。 そしお最も重芁なこずは、圌らの理解、倉曎、保守のための時間を短瞮するこずです。





クロヌゞュア岩



すぐに譊告したす-Clojureのクヌルさを瀺すコヌドスニペットはありたせん。 「X蚀語では5行かかり、Clojureでは4行しかかからなかった」ずいったフレヌズはありたせん。 これは、蚀語の品質に関する嫌な基準です 最終的に、 qsort



を2行で蚘述できるかどうかは気にしたせん。たたは、5本も指をqsort



なければなりたせん。実際には、ラむブラリ関数を䜿甚したす。



ラムダを持っおいる人を驚かせるこずはありたせん。それらはどこにでもありたす通垞、ほずんどの堎合、8番目のバヌゞョンではどこにでも衚瀺されたす。 コレクション䞊列を含む、リスト匏、さたざたな構文糖の凊理-倚くの蚀語でこれで十分です。 実のずころ、私はそのような蚘事をただ厇拝しおいたす。 しかし、このような比范は、蚀語の品質を比范するのにはたったく䞍適切です これは、プログラムが「Hello、world」ず衚瀺する速さによっおYPの速床を枬定する方法です。 さお、 HQ9 +の速床を枬定しない限り。 考えおみるず、このような詳现は倧芏暡なシステムにずっおそれほど重芁ではありたせん。 プロゞェクトが成長するに぀れお、括匧やむンデントを䜿甚するか、䞭眮蚘法、接頭蟞蚘法のどちらを䜿甚するかに぀いお、私たちはたすたす心配しなくなりたす。 配列の合蚈を芋぀けるずきの䜙分な行は、すでにすべおの人を悩たすこずをやめたす-そもそも、異なる皮類の問題が出おきたす。



難易床



私たちが䜜成するシステムは本質的に揮発性です。 芁件が倉曎されなければ、非垞に良いでしょう。 開発の最初の段階ですべおの状況を事前に予枬できるずしたら、それは玠晎らしいこずです。 悲しいかな、実際には、私たちは垞に仕䞊げ、改造、改善、曞き換え、亀換、最適化する必芁がありたす...最も䞍快なこずは、時間が経぀に぀れおシステムの耇雑さが増すこずです。 絶えず、継続的に。 開発の初期段階では、すべおがシンプルで透過的であり、倉曎はすべお「クランチ」なしで迅速に行われたす。 矎人 時間が経぀に぀れお、状況はずおもバラ色で陜気になりたす。 コヌドのわずかな線集でも、システムの動䜜に雪厩のような倉化が生じる可胜性がありたす。 慎重に研究し、コヌドを分析し、各倉曎による副䜜甚を予枬する必芁がありたす。 正確に蚀えば、時間が経぀に぀れお、文字通り、倉曎のすべおの可胜な結果を​​完党に分析するこずはできたせん。



人は本来、限られた量の情報しか䞀床に知芚できたせん。 プロゞェクトが成長するに぀れお、内郚接続の数も増加したす。 さらに、ほずんどの接続は暗黙的です。 頭の䞭で必芁なものを維持するこずがたすたす困難になっおいたす。 そしお珟時点では、チヌムは成長しおおり、チヌムは倉化しおいたす-新しい人々はプロゞェクト党䜓をもはや知りたせん。 責任が分離されおいるため、さらに混乱する可胜性がありたす。 埐々に、システムが耇雑になりたす。



これに察凊する方法は 回垰テストの最倧カバレッゞず各倉曎埌にそれらを実行したすか テストは非垞に䟿利ですが、安党ロヌプにすぎたせん。 テストはパスしたせんでした-䜕かが正しくありたせん。問題がありたす。 これは症状の治療ですが、テストは問題の本質を解決したせん。 厳栌なガむドラむンずパタヌンの広範な䜿甚 いいえ、問題は地元の問題ではありたせん。 コヌド内でコンポヌネントがどのように盞互䜜甚するかを理解するのをやめるだけです。暗黙的なリンクが倚すぎたす。 たぶん䞀定のリファクタリングですか これは䞇胜薬ではありたせん;䜎レベルの゜リュヌションから耇雑さは増したせん。 実際、問題は包括的に察凊する必芁がありたす。 そしお、重芁なツヌルの1぀は適切なツヌルです。 優れたプログラミング蚀語は、シンプルで透過的なプログラムを䜜成するのに圹立ちたす。



シンプルで簡単



しかし、「シンプル」ずは「簡単」ずいう意味ではありたせん。 これらは異なる抂念です。 このテヌマで、Rich HickeyClojureの著者は有名なレポヌトSimple Made Easyを䜜成したした 。 スラむドの翻蚳は Habr で公開されおいたす。 シンプルさは客芳的な抂念です。 この耇雑さの欠劂耇雑さ、織り蟌みの欠劂、混乱、少数の接続。 䞀方、「簡単」は非垞に䞻芳的です。 自転車に乗るこずは簡単ですか チェスの詊合に勝ちたすか ドむツ語を話す ドむツ語はわかりたせんが、これは「この蚀語は必芁ない、耇雑すぎる」ず蚀う理由ではありたせん。 それは私にずっお難しいこずであり、それでも私は単にそれを知らないからです。



私たちはすべお、関数呌び出しf(x, y)



呌び出しに慣れおいたす。 私たちは、OOPのフレヌムワヌクでのプログラミングに慣れおいたす。 これは圓たり前です。 しかし実際、肺は必ずしも単玔ではありたせん。 私たちは、あるものの耇雑さに慣れ、それを無芖し始め、圓たり前だず思っおいたす。 関数の䟋



 (defn select "Returns a set of the elements for which pred is true" {:added "1.0"} [pred xset] (reduce (fn [sk] (if (pred k) s (disj sk))) xset xset))
      
      







それは非垞に... 奇劙に芋えたす 蚀語を習埗し、その抂念を習埗しお、蚀語を簡単にする必芁がありたす。 しかし、単玔さたたは耇雑さは䞀定です。 ツヌルをよく調べれば、内郚䟝存関係の数はずにかく倉わりたせん。 私たちにずっおは簡単ですが、より耇雑でも単玔でもありたせん。



䜿い慣れたツヌルを䜿甚するず、すぐに最良の結果をすぐに埗るこずができたすが、長期的には、最も単玔な゜リュヌションが最良の結果を瀺したす。



副䜜甚



プログラムの難しさの原因は䜕ですか それらの1぀は副䜜甚です。 それらなしでは完党に行うこずはできたせんが、ロヌカラむズするこずはできたす。 そしお、蚀語はこれに圹立぀はずです。



Clojureは関数型蚀語であり、玔粋な関数を曞くこずを奚励しおいたす。 このような関数の結果は、入力パラメヌタヌのみに䟝存したす。 「うヌん、でも、この関数を呌び出す前にこれを起動するずどうなりたすか」ず頭を悩たす必芁はありたせん。 入力がある堎合は、週末がありたす。 関数を䜕床実行しおも、結果は同じになりたす。 これにより、テストが非垞に簡単になりたす。 呌び出しのさたざたな順序を゜ヌトしたり、正しい倖郚状態を再䜜成シミュレヌションする必芁はありたせん。



玔粋な関数は分析が簡単で、文字通り「遊び」、ラむブデヌタでの動䜜を確認できたす。 デバッグしやすいコヌド。 関数の結果は以前に実行されたものに䟝存しないため、゚ラヌを匕き起こすパラメヌタヌを枡すだけで、玔粋な関数でい぀でも問題を再珟できたす。 玔粋な機胜は、倚くの䜜業を行う堎合でも非垞に簡単です。



もちろん、Clojureは高階関数、その構成をサポヌトしおいたす。



 ((juxt dec inc) 1) ; => [0 2] ((comp str *) 1 2 3) ; => "6" (map (partial * 10) [1 2 3]) ; => [10 20 30] (map (comp inc inc) [1 2 3]) ; => [3 4 5]
      
      







Clojureは玔粋な蚀語ではなく、関数には副䜜甚がありたす。 たずえば、 println



は関数呌び出し、アクションです。 そのような機胜の本質が倖の䞖界ずの盞互䜜甚にあるこずが重芁です。 ファむルに倀を出力するには、HTTPリク゚ストを送信し、SQLを実行したす。これらのアクションはすべお、それらが䜜成する副䜜甚ずは別に無意味です。 したがっお、このような機胜クリヌンずダヌティを分離するこずは非垞に䟿利です。



ただし、それらダヌティ関数には状態がありたせん。 それらは倖の䞖界ずの盞互䜜甚の手段ずしおのみ機胜したす。 埌で芋るように、Clojureは間接リンクを䜿甚しおプログラムの状態を分離したす。



免疫



Clojureのすべおのデヌタ構造は䞍倉です。 ベクタヌ芁玠を倉曎する方法はありたせん。 できるこずは、1぀の芁玠が倉曎された新しいベクトルを䜜成するこずだけです。 非垞に重芁な点は、Clojureがすべおの暙準的な収集操䜜に察しお時間ずメモリの点でアルゎリズムの耇雑さを保持しおいるこずです。 たあ、ほずんど、ベクトルのO1の代わりに、Olog 32 Nがありたす。 実際には、数癟䞇の芁玠のコレクションでさえ、lg 32 Nは5を超えたせん。



この耇雑さは、 氞続的なコレクションを䜿甚しお実珟されたす。 構造が「倉曎」されるず、叀いバヌゞョンず新しいバヌゞョンが内郚デヌタのほずんどを共有するずいう考え方です。 同時に、叀いバヌゞョンは完党に機胜し続けたす。 さらに、構造のすべおのバヌゞョンにアクセスできたす。 これは重芁なポむントです。 もちろん、䞍芁なバヌゞョンはガベヌゞコレクタヌによっお収集されたす。



 (def a [1 2 3 4 5 6 7 8]) ; a -> [1 2 3 4 5 6 7 8] (def b (assoc a 3 999)) ;b -> [1 2 3 999 5 6 7 8]
      
      







すぐに䜿えるClojureは、単䞀リンクリスト、ベクトル、ハッシュテヌブル、赀黒朚をサポヌトしおいたす。 氞続キュヌの実装がありたすスタックにはリストたたはベクタヌを䜿甚できたす。 そしお、すべおは䞍倉です。 パフォヌマンスを向䞊させるために、独自のレコヌドタむプを䜜成できたす。



 (defrecord Color [red green blue]) (def a (Color. 0.5 0.6 0.7) ; a => {:red 0.5, :green 0.6, :blue 0.7}
      
      







ここでは、3フィヌルド構造を宣蚀したす。 Clojureコンパむラヌは、5぀のフィヌルド2぀の「远加」を持぀オブゞェクトを䜜成したす。 メタデヌタ甚の1぀のフィヌルド。この䟋ではnullです。 実際のデヌタ甚の3぀のフィヌルド。 そしおもう1぀のフィヌルド-远加キヌ甚。 プログラムの速床を䞊げるために、フィヌルドを明瀺的に列挙した構造䜓を宣蚀したずしおも、Clojureはさらに倀を远加する機䌚を残しおいたす。



 (defrecord Color [red green blue]) (def b (assoc a :alpha 0.1)) ; b => {:alpha 0.1, :red 0.5 :green 0.6, :blue 0.7}
      
      







はい、Clojureのデヌタ構造には特別な構文がありたす。



 ;  [1 2 3] ; - {:x 1, :y 2} ;  #{"a" "b" "c"}
      
      







状態



したがっお、玔粋な関数があり、アプリケヌションのビゞネスロゞックを決定したす。 倖郚システム゜ケット、デヌタベヌス、Webサヌバヌでの察話に䜿甚されるダヌティ関数がありたす。 そしお、システムの内郚状態があり、間接参照ずしおClojureに保存されたす。



暙準リンクには4぀のタむプがありたす。





すべおのグロヌバル倉数はvar



関数を含むに保存されたす。 したがっお、「ロヌカル」に再定矩できたす。



 (def ^:dynamic *a* 1) (println a) ; => 1 (binding [a 42] (println a)) ; => 42
      
      







ここで、倉数a



は動的でa



必芁があるこずをコンパむラヌに指摘したした。 ThreadLocal内に保存されたす。 ThreadLocal



を䜿甚するずパフォヌマンスがわずかに䜎䞋するため、デフォルトではすべおのvar



セルに適甚されるわけではありたせん。 ただし、必芁に応じお、䜜成埌に任意のvar



動的にするこずができたすテストでよく䜿甚されたす。



テストでは、関数党䜓を眮き換えるこずができたす。



 ;     ,   .. (defn some-function-with-side-effect [x] ...) ;       (defn another-function [x] ...) (deftest just-a-test ... (binding [some-function-with-side-effect (fn [x] ...)] ;  mock- (another-function 123)) ...)
      
      







Clojureのすべおのリンクは、 deref



倀の取埗操䜜をサポヌトしおいたす。 var



セルの堎合、次のようになりたす。



 ;   #'a (def a 123) (println a) ; => 123 (println #'a) ; => #'user/a (println (deref #'a)) ; => 123
      
      







セルは倀䞍倉を栌玍したすが、それ自䜓は独立した゚ンティティです。 deref



関数に特別な構文が導入deref



れたしたはい、それはただの砂糖です。 atom



の䜿甚䟋を次に瀺したす。



 (let [x (atom 0)] (println @x) ; => 0 (swap! x inc) ; CAS- (println @x)) ; => 1
      
      







swap!



機胜swap!



アトムず「倉化」関数を取りたす。 埌者はアトムの珟圚の倀を取埗し、新しい倀を返す必芁がありたす。 ここでは、氞続的なデヌタ構造が非垞に䟿利です。 たずえば、100䞇個の芁玠のベクトルをアトムに栌玍できたすが、「倉曎」関数はCASに十分な速さで実行されたす氞続コレクションの操䜜の耇雑さは、通垞の倉曎可胜なものず同じであるこずを芚えおいたす。 たたは、ハッシュテヌブルのいく぀かのフィヌルドを曎新できたす。



 (def user (atom {:login "theuser" :email "theuser@example.com"})) (swap! account assoc :phone "12345") ;    (swap! account (fn [x] (assoc x :phone "12345")))
      
      







関数は数回実行できるため、関数がクリヌンであるこずが重芁です。 次のような蚘述はできたせんすべきではありたせん。



 (swap! x (fn [x] (insert-new-record-to-db x) (inc x)))
      
      







゚ヌゞェント



゚ヌゞェントは、副䜜甚に盎接関係する状態をサポヌトする圹割を果たしたす。 アむデアはシンプルです。 セルがあり、関数のキュヌがそれに「接続」されおいたす。 このセルに栌玍されおいる倀に関数が1぀ず぀適甚され、関数の結果が新しい倀になりたす。 すべおが個別のスレッドプヌルで非同期的に蚈算されたす。



 (def a (agent 0)) ;   (send a inc) (println @a) ; => 1 (send a (fn [x] (Thread/sleep 100) (inc x))) (println @a) ; => 1 ;  100  (println @a) ; => 2
      
      







゚ヌゞェントは倀を非同期に曎新したす。 ただし、゚ヌゞェントの状態はい぀でも確認できたす。 ゚ヌゞェントは互いにメッセヌゞを送信できたす;送信するずき、送信゚ヌゞェントがステヌタスを曎新するたでメッセヌゞは遅延したす。 ぀たり、ある゚ヌゞェントで䟋倖がスロヌされた堎合、そこから送信されたメッセヌゞはどこにも送信されたせん。



 (def a (agent 0)) (def b (agent 0)) (send a (fn [x] (send b inc) ;    b (throw (Exception. "Error")))) (println @b) ; -> 0,     
      
      







俳優のモデルずのある類掚は、 それ自䜓を瀺唆しおいたす 。 これらは䌌おいたすが、根本的な違いがありたす。 ゚ヌゞェントの状態は明瀺的であり、い぀でもderef



を呌び出しお゚ヌゞェントの倀を取埗できたす。 これは、メッセヌゞを送受信するこずで間接的にしか状態を知るこずができないアクタヌの抂念ず矛盟しおいたす。 俳優の堎合、圌の状態にむンタビュヌしたこずを確信するこずさえできたせん。私たちは「偶然」それを倉えないでしょう。 この意味で、゚ヌゞェントは完党に信頌できたす。その状態は、 send



およびsend-off



関数メッセヌゞが凊理されるスレッドプヌルのみが異なるでのみ倉曎できたす。



2番目の重芁な違いは、゚ヌゞェントが機胜を倉曎および远加できるこずです。 アクタヌの動䜜を倉曎する唯䞀の方法は、コヌドを曞き盎すこずです。 ゚ヌゞェントは単なるリンクであり、独自の動䜜はありたせん。 新しい関数を䜜成しお、゚ヌゞェントキュヌに送信できたす。



アクタヌは、プログラムの状態を、バラバラにしたり分離したりしやすい小さな郚分に分割しようずしおいたす。 ステヌタスの曎新および読み取り操䜜は、メッセヌゞの送信になりたす。 これは非垞に䟿利な堎合がありたすたずえば、耇数のノヌドでerlangプログラムを実行する堎合。 しかし、倚くの堎合、これは必芁ありたせん。 時にはその逆です。 したがっお、゚ヌゞェントは、キャッシュ、セッション、数孊的蚈算の䞭間結果など、フロヌ間を移動するために必芁な倧量の情報を保存するず䟿利です。



アクタヌに぀いおは、圌が応答できる倚くのメッセヌゞを修正したす圌は残りを誀っおいるず考えおいたす。 メッセヌゞの順序も重芁であり、朜圚的に副䜜甚がある可胜性がありたす。 これは圌の公開契玄です。 ゚ヌゞェントの堎合、保存できるデヌタ、その構造のみを蚘録したす。 ゚ヌゞェントがアクタヌを眮き換えるこずをたったく詊みおいないこずを匷調するこずは非垞に重芁です。 これらは異なる抂念であり、その範囲は異なりたす。



前述のように、゚ヌゞェントは非同期に動䜜したす。 むベントのチェヌン゚ヌゞェントから゚ヌゞェントぞのメッセヌゞの送信を構築できたす。 しかし、゚ヌゞェントの助けだけでは、プログラムの状態を調敎された方法で倉曎するこずはできたせん。



STM



゜フトりェアトランザクションメモリは、Clojureの䞻芁な機胜の1぀です。 MVCCによっお実装されたす。 そしおすぐに䟋



 (def account1 (ref 100) (def account2 (ref 0)) (dosync (alter account1 - 30) (alter account2 + 30))
      
      







1぀の倀を増やし、同時に別の倀を枛らしたす。 䜕か問題が発生した堎合䟋倖、トランザクション党䜓がキャンセルされたす。



 (println @account1) ; => 70 (println @account2) ; => 30 (dosync (alter account1 * 0) (alter account2 / 0)) ; => ArithmeticException ;    (println @account1) ; => 70 (println @account2) ; => 30
      
      







通垞のACIDず非垞に䌌おいたすが、耐久性がありたせん。 トランザクションを入力するず、すべおのリンクがフリヌズしおいるように芋え、それらの倀はトランザクション党䜓の期間䞭固定されたす。 リンクの読み取り/曞き蟌み時に、その倀が既に倉曎されおいる他のトランザクションが完了し、私たちの生掻を台無しにしおいるこずが刀明した堎合、珟圚のトランザクションが再開されたす。 したがっお、トランザクション内での副䜜甚入出力、アトムの操䜜はありたせん。 そしお、ここで゚ヌゞェントが圹に立ちたす。



 (def a (ref 0)) (def b (ref 0)) (def out-agent (agent nil)) (dosync (println "transaction") (alter a inc) ;      (let [a-value @a b-value @b] (send-off out-agent (fn [_] (println "a" a-value "b" b-value)))) (alter b dec)) ;     
      
      







゚ヌゞェントぞのすべおのメッセヌゞは、トランザクションが完了した瞬間に埓いたす。 この䟋では、リンクa



ずb



倉曎a



ずトランザクションが再開される堎合があり、「トランザクション」ずいう単語を数回印刷できたす。 ただし、゚ヌゞェント内のコヌドは、トランザクションが完了した埌に䞀床だけ実行されたす。



さたざたなトランザクションが互いに干枉しないように、Clojureのリンクは倀の履歎を保持したす。 デフォルトでは、これは最埌の倀のみですが、競合が発生するず1぀のトランザクションの曞き蟌みず他の読み取り、特定のリンクに察しお、保存された履歎のサむズが1぀最倧5぀の倀増加したす。 共通の構造芁玠を共有するリンクに氞続構造を保存するこずを忘れないでください。 したがっお、このようなストヌリヌをClojureに保存するこずは、メモリ消費の芳点から非垞に安䟡です。



STMトランザクションは、コヌドを倉曎するずきに気にしたせん。 珟圚のトランザクションで1぀たたは別のリンクを䜿甚できるかどうかを分析する必芁はありたせん。 それらはすべお利甚可胜であり、既存のコヌドに完党に透過的に新しいリンクを远加できたす。 リンクは盞互に䜜甚したせん。 たずえば、通垞のロックを䜿甚する堎合、デッドロックが発生しないようにロック/ロック解陀の順序を監芖する必芁がありたす。



同時アクセスでは、 ReadWriteLockを䜿甚するのず同じように、リヌダヌトランザクションは互いにブロックしたせん。 さらに、トランザクションラむタヌはリヌダヌをブロックしたせん リンクを倉曎するトランザクションが珟圚実行されおいる堎合でも、ブロックせずに倀を取埗できたす。



゚ヌゞェントずSTMリンクは互いに補完したす。 前者は調敎された状態の倉化には適しおいたせんが、埌者は副䜜甚を扱うこずはできたせん。 しかし、それらの共同䜿甚により、「叀兞的な」ツヌルミュヌテックス、セマフォなどを䜿甚するよりも、プログラムがより透明で単玔耇雑ではないになりたす。



メタプログラミング



珟圚、倚くの蚀語に䜕らかのメタプログラミングツヌルがありたす。 これらは、Java甚のAspectJ 、Groovy甚のAST倉換、Python甚のデコレヌタヌずメタクラス、さたざたなリフレクションです。



Clojureは、Lispファミリヌのメンバヌずしお、これらの目的でマクロを䜿甚したす。圌らの助けを借りお、蚀語自䜓を䜿っお蚀語をプログラム拡匵できたす。マクロは「通垞の」関数ですが、唯䞀の違いはプログラムのコンパむル䞭に実行されるこずです。ただコンパむルされたコヌドはマクロ入力に枡されたせん。マクロの実行結果は、コンパむラが既にコンパむルした新しいコヌドです。



 (defmacro unless [pred ab] `(if (not ~pred) ~a ~b)) (unless (> 1 10) (println "1 > 10. ok") (println "1 < 10. wat"))
      
      







独自の制埡構造逆バヌゞョンif



を䜜成したした。あなたがする必芁があるのは、関数を曞くこずです



マクロはClojureで広く䜿甚されおいたす。ずころで、蚀語に組み蟌たれおいる挔算子の倚くは、実際にはマクロです。たずえば、以䞋は実装or



です。



 (defmacro or ([] nil) ([x] x) ([x & next] `(let [or# ~x] (if or# or# (or ~@next)))))
      
      







でもdefn



ただマクロに展開def



しおfn



。ちなみに、構造化もマクロを䜿甚しお実装されたす。



 (let [[ab] [1 2]] (+ ab)) ;   - ... (let* [vec__123 [1 2] a (clojure.core/nth vec__123 0 nil) b (clojure.core/nth vec__123 1 nil)] (+ ab))
      
      







最近、try-with-resourcesが Javaに登堎したした。同時に、Javaの第7バヌゞョンが登堎するのをほんの数幎埅ちたした。Clojureの堎合は、数行曞くだけです。



 (defmacro with-open [[vr] & body] `(let [~r ~v] (try ~@body (finally (.close ~v)))))
      
      







他の蚀語では、状況は良くなりたすが、それでも理想からはほど遠いです。重芁なこずは、蚀語に特定の構造が存圚するこずではなく、独自の構造を远加する胜力です。したがっお、たずえば、Clojureのパタヌンマッチングが個別のプラグむンラむブラリずしお実装されおいるこずは驚くこずではありたせん。そのようなものを蚀語のコアに含める必芁はたったくありたせん。それらをマクロの圢で実装する方がはるかに䟿利です。状況は、モナドのサポヌト、論理プログラミング、高床な゚ラヌ凊理、およびその他の蚀語拡匵ず同様です。オプションの静的型付けもありたす



DSLを䜜成するこずの利䟿性に蚀及するしかありたせん。 Clojureにはたくさんありたす。これずHTML生成およびHTTP-芁求のルヌティングずはで動䜜するリレヌショナル デヌタベヌスの デヌタ、及びでの䜜業バむナリプロトコルず怜蚌デヌタこの堎合は、あなたが停止する際に知っおおく必芁がありたすが...簡単か぀効果的に䜜成したす。



ClojureすべおのLispラむクな蚀語ず同様には非垞に重芁な機胜がありたす-それは同皮ナニヌクです。蚀い換えるず、プログラムの゜ヌスコヌド甚に別のプレれンテヌションを䜜成する必芁はありたせん。䜙分なプレれンテヌションを䜜成する必芁はありたせん。いく぀かの远加のASTツリヌの圢での抜象化のレベル、プログラムはこのツリヌです。さらに、このツリヌは特別な構造ではなく、通垞のリスト、ベクトル、およびシンボルです。そしお、通垞のデヌタず同じ方法でプログラムを操䜜できたす。



 (defn do2 [x] (list 'do xx)) (do2 '(println 1)) ; => '(do (println 1) (println 1)) ;   ; => (list 'do (list 'println 1) (list 'println 1))
      
      







Clojureのマクロは、その力のすべおに぀いお、プログラムの可読性を損ないたせんもちろん、適床に䜿甚する堎合を陀きたす。結局のずころ、マクロは単なる関数であり、珟圚のコンテキストでどの関数を䜿甚するかを垞に䞀意に決定できたす。たずえば、コヌドを芋るず、(dosomething [ab] c)



名前の埌ろに隠れおいるものを芋぀けるのは簡単dosomething



です。ファむルの先頭他のモゞュヌルがむンポヌトされおいる堎所を芋るだけです。これがマクロの堎合、そのセマンティクスは䞀定であり、よく知られおいたす。このコヌドを理解するのに高床なIDEは必芁ありたせん。もちろん、高床な開発環境ではマクロを適切に「展開」するこずができたす。これにより、コンパむラヌがプログラムをどのように倉えるかを確認できたす。



倚型



Clojureには、ポリモヌフィック関数を䜜成するための2぀のメカニズムがありたす。圓初、この蚀語は匷力なツヌルであるマルチメ゜ッドのみをサポヌトしおいたしたが、ほずんどの堎合は過剰でした。バヌゞョン1.2およびバヌゞョン1.5.1が珟圚関連から、新しい抂念が蚀語に远加されたした-プロトコル。



プロトコルはJavaむンタヌフェヌスに䌌おいたすが、互いに継承するこずはできたせん。各プロトコルには、䞀連の機胜が蚘述されおいたす。



 (defprotocol IShowable (show [this])) ; ... (map show [1 2 3])
      
      







これにより、プロトコル自䜓ず関数の2぀の゚ンティティを宣蚀したすshow



。これは通垞のClojure関数で、呌び出されるず、最初の匕数の型に基づいお最も適切な実装を怜玢したす。別途、必芁なデヌタ構造を宣蚀し、それらにプロトコルの実装を瀺したす。



 (defrecord Color [red green blue] IShowable (show [this] (str "<R" (:red this) " G" (:green this) " B" (:blue this))))
      
      







サヌドパヌティタむプのプロトコルを実装できたすビルトむンであっおも。



 (extend-protocol IShowable String (show [this] (str "string " this)) clojure.lang.IPersistentVector (show [this] (str "vector " this)) Object (show [this] "WAT")) (show "123") ; => "string 123" (show [1 2 3]) ; => "vector [1 2 3]" (show '(1 2 3)) ; => "WAT"
      
      







゜ヌスコヌドにアクセスできない堎合でも、既存のタむプにプロトコル実装を远加できたす。バむトコヌドたたは同様のトリックによる魔法の操䜜はありたせん。 Clojureはグロヌバルテヌブルを䜜成したす ->



。プロトコルメ゜ッドが呌び出されるず、階局を考慮しお、このテヌブルで最初の匕数のタむプによっお怜玢されたす。したがっお、プロトコルの新しい実装の宣蚀は、グロヌバルテヌブルの曎新に垰着したす。



ただし、プロトコルでは䞍十分な堎合がありたす。たずえば、二重スケゞュヌリングの堎合。この堎合だけでなく、マルチメ゜ッドが䟿利になりたす。。マルチメ゜ッドを宣蚀するずき、特別なサむド関数ディスパッチャヌを指定したす。ディスパッチャヌは、マルチメ゜ッドず同じ匕数を受け取りたす。最終実装の怜玢は、ディスパッチャによっお返された倀によっお既に実行されおいたす。タむプ、キヌワヌド、たたはベクトルを指定できたす。ベクトルの堎合、いく぀かの倀を䜿甚しお最適な実装を怜玢したす。



 (defmulti convert (fn [obj target-type] [(class obj) target-type])) (defmethod convert [String Integer] [x _] (Integer/parseInt x)) (defmethod convert [String Long] [x _] (Long/parseLong x)) (defmethod convert [Object String] [x _] (.toString x)) (defmethod convert [java.util.List String] [x _] (str "V" (vec x))) (convert "123" Integer) ; -> 123 (convert "123" Long) ; -> 123 (convert 123 String) ; -> "123" (convert [1 2 3] String) ; -> "V[1 2 3]"
      
      







ここで、最初の匕数の型ず2番目の匕数の倀これはクラスでなければなりたせんに基づいお実装が遞択される抜象関数を発衚したした。もちろん、Clojureは適切な実装を探す際に型階局を考慮したす。タむプは䜿いやすいですが、階局は厳密に固定されおいたす。ただし、キヌワヌドから独自のアドホック階局を䜜成できたす。



 ;   "-" (derive ::rect ::shape) (derive ::square ::rect) (derive ::circle ::shape) (derive ::triangle ::shape) (defmulti perimeter :type) ;       ,  :type ~ (fn [x] (:type x)) (defmethod perimeter ::rect [x] (* 2 (+ (:hx) (:wx)))) (defmethod perimeter ::triangle [x] (reduce + ((juxt :a :b :c) x))) (defmethod perimeter ::circle [x] (* 2 Math/PI (:rx))) (perimeter {:type ::rect, :h 10, :w 3}) ; -> 26 (perimeter {:type ::square, :h 10, :w 10}) ; -> 40 (perimeter {:type ::triangle, :a 3, :b 4, :c 5}) ; -> 12 (perimeter {:type ::shape}) ; -> throws IllegalArgumentException
      
      







耇数の階局を宣蚀できたす。型ず同様に、耇数の倀を䞀床にディスパッチできたすベクタヌ。独自の階局を定矩する堎合、キヌワヌドずJavaタむプを混圚させるこずもできたす



 (derive java.util.Map ::collection) (derive java.util.Collection ::collection) (derive ::tag .java.lang.Iterable) ; -> ClassCastException
      
      







キヌワヌドから型を「継承」できたす逆も同様です。これは、拡匵が可胜なクラスグルヌプを䜜成するのに䟿利です。



マルチメ゜ッドシステムはシンプルですが、非垞に匷力です。通垞、日垞的なニヌズにはプロトコルの機胜で十分ですが、耇雑な非暙準の状況ではマルチメ゜ッドが優れた゜リュヌションになりたす。



åžžè­˜



蚀語は、むンフラストラクチャなしでは意味がありたせん。コミュニティ、ラむブラリ、フレヌムワヌク、さたざたなナヌティリティのセットなし。 Clojureの匷みの1぀は、JVMプラットフォヌムの䜿甚です。 Javaずの統合双方向は非垞に簡単です。 Javaには膚倧な数のラむブラリが存圚するこずは秘密ではありたせんその品質に぀いおは説明したせん。それらはすべおClojureから盎接䜿甚できたす。ただし、ネむティブラむブラリの数は非垞に倚く垞に増加しおいたすいたす。EclipseずIDEAの



プラグむンは掻発に開発されおいたす。プロゞェクトを構築するために、コミュニティ党䜓で䜿甚されおいるラむンニングナヌティリティは、長い間事実䞊の暙準でした。䜜成するためのさたざたなフレヌムワヌクがありたす WEBアプリケヌションず非同期サヌバヌ 䞍倉の



アプリケヌションサヌバヌが開発されおいたすJBoss AS7のラッパヌ。 Immutantは、リングClojureのHTTPスタック、非同期メッセヌゞ、分散キャッシュ、スケゞュヌルされたタスク、分散トランザクション、クラスタリングなどを操䜜するためのむンタヌフェヌスを提䟛したす。 Immutantの展開ず構成は非垞に簡単です。



Clojureには、.Net CLRの䞋のポヌトなどの代替実装もありたす。しかし、真実は蚀われたす、ClojureScriptは最も泚目に倀する、JavaScript甚のポヌト。もちろん、マルチスレッドツヌルはありたせん。そのため、トランザクションメモリず゚ヌゞェントがありたす。ただし、氞続構造、マクロ、プロトコル、マルチメ゜ッドなど、他のすべおの蚀語機胜を䜿甚できたす。たた、ClojureScriptずJavaScriptの統合は、ClojureずJavaの統合ず同じくらい優れおおり、単玔です堎所によっおはさらに優れおいたす。



次は



そしお、すべおが簡単です。ツヌルがありたす。劎働者、信頌できる。特効薬ではありたせんが、十分な汎甚性がありたす。シンプル。はい、あなたはそれを習埗するためにいく぀かの時間を費やす必芁があるかもしれたせん。倚くは珍しくお奇劙に芋えるかもしれたせん。しかし、これは習慣の問題にすぎたせん。蚀語の党䜓の矎しさは、個々の芁玠が単䞀の党䜓に繊现に結合しおいるずいう有機性にあるこずにすぐに気付くでしょう。



Clojureを知るこずは䟡倀がありたす。間違いなく。 このツヌルが䜕らかの理由であなたに合わない堎合でも、そこに組み蟌たれおいるアむデアは非垞に有甚であるこずが蚌明されたす。



All Articles