web-dev-qa-db-ja.com

なぜClojureでMaybe / Optionの使用がそれほど普及していないのですか?

関数型パラダイムに重点を置いているにもかかわらず、Clojureがオプションの値を表すためにMaybe/Optionモナドを使用しないのはなぜですか? Optionの使用は、私が定期的に使用する関数型プログラミング言語であるScalaで非常に普及しています。

42
missingfaktor

Clojureは静的に型付けされていないので、haskell(そして私が集めたScala)で必要な厳密なthis/that/whatever型宣言は必要ありません。文字列を返したい場合は、文字列を返します。代わりにnilを返す場合は、それも問題ありません。

「機能的」は「厳密なコンパイル時の型付け」に正確に対応していません。それらは直交する概念であり、Clojureは動的型付けを選択します。実際、かなり長い間、mapのような高階関数の多くを実装し、静的型付けを維持する方法を想像できませんでした。 Haskellの経験が少し(ごくわずか)になったので、それが可能であり、実際に非常にエレガントであることがよくわかります。 Clojureでしばらく遊んでいると、逆の経験になると思います。型宣言がそうではないことに気付くでしょう必要あなたが持っていることに慣れている種類の力を与えるために関数型言語で。

49
amalloy

Clojureでは、nil punningは、Scala&HaskellがOption&Maybeから取得する機能のほとんどを提供します。

**Scala**                **Clojure**
Some(1) map (2+_)        (if-let [a 1] (+ 2 a))

Some(1) match {          (if-let [a 1]
  case Some(x) => 2+x      (+ 2 a)
  case None    => 4        4)
}

ScalaのOptionとHaskellのMaybeは、どちらもApplicativeのインスタンスです。これは、これらのタイプの値を内包表記で使用できることを意味します。たとえば、Scalaは以下をサポートします:

for { a <- Some(1)
      b <- Some(2)
} yield a + b

マクロ用のClojureは、seqに関する理解を提供します。モナド内包表記とは異なり、この実装ではインスタンスタイプを混在させることができます。

Clojureのforは、複数の可能なnil値で関数を作成するために使用することはできませんが、実装するのは簡単な機能です。

(defn appun [f & ms]
  (when (every? some? ms)
    (apply f ms)))

そしてそれを呼ぶ:

(appun + 1 2 3)    #_=> 6
(appun + 1 2 nil)  #_=> nil
26
agarman

モナドの概念は型に関するものではないことを覚えておくことが重要です!型システムはルールの施行に役立ちます(ただし、Haskellでさえすべてのルールを施行することはできません。一部のルール(モナド法)は型システムで完全に表現できないためです。

モナドは作曲に関するものであり、これは私たち全員がすべてのプログラミング言語で毎日行う非常に重要なことです。すべての場合において、モナドは何が起こっているかについての「余分なコンテキスト」を追跡します...現在の値を保持するボックスのように考えてください。関数はこの値に適用でき、追加のコンテキストは直交する懸念として進化する可能性があります。

多分タイプは、失敗について何も言う必要がなく、長い一連の計算を連鎖させることです(これは「余分なコンテキスト」です)。これは、「エラー処理」を計算からモナドに移動するパターンです。たぶん、計算のシーケンスを文字列化することができ、1つが失敗するとすぐに、残りは無視され、最終結果は「何もない」になります。それらがすべて成功した場合、最終結果は結果値を保持するモナドになります。

これにより、絡みの少ないコードを記述できます。

@deterbが指摘したように、ClojureはMonadsをサポートしています。

17
Tony K.

多分/オプションはタイプです。関数型プログラミングとは何の関係もありません。はい、一部の言語(Scala、haskell、ocaml)は、機能的であるだけでなく、非常に強力な型システムも提供します。人々はhaskellについてそれがTYPESを使ったプログラミングであるとさえ言います。

その他(clojure、LISP)は、完全に機能する関数型言語であるにもかかわらず、タイプに関して多くを提供しません。それらの強調は異なり、多分/オプションタイプは適合しません。それは単に動的言語ではあまり得られません。たとえば、シーケンス(リスト、ベクトル、マップ)を操作する多くのclojure関数は、null(nil)を完全に受け入れ、それを空の構造として扱います。

(count nil)はあなたに0を与えます。ちょうど(count [])のように

Clojureは「型を使ったプログラミング」とは言えないので、おそらく型はあまり意味がありません。

13
Vagif Verdi

たぶんモナドがありますが、nilをNothingとして使用し、計算の抽象化をキャプチャしますonly(input = nilがnilを返す場合、elseはinputで何でも計算します)ヌルポインタエラーを回避するためですが、静的なコンパイル時の安全性はありません。同様の使命を持っている fnil もあり、デフォルト値でnilにパッチを適用し、 -?> です。 clojureの方法は、エラーまたはnilを発生させるデフォルト値を返すことを目的としていると思います。

7
jneira

@amalloyを使用して、Clojureは言語として、オプションの戻り値は必要ないとコメントしています。

私はScalaをあまり使っていませんが、Clojureは、値を操作できるようにするために、戻り値の型に関する厳密な詳細を知る必要はありません。たぶんモナドが埋め込まれ、通常のClojure評価の一部になったかのようです。多くの操作が、nilで実行された場合、nilを返します。

Clojure-Contribライブラリをざっと見てみたところ、 モナドパッケージ があります。 Clojureのモナドをどのように利用するかについて私を本当に手がかりにしたもう1つの項目は、 Clojureのモナドに関するCosminのチュートリアル です。これが、Scala)でより明示的に記述されている機能が動的Clojure言語の一部としてどのように処理されるかを結び付けるのに役立ちました。

5
deterb

有る some->およびsome->> Clojure1.5以降で利用可能

(let [input {:a 1 :b 2 :c {:d 4 :e 5 :f {:h 7}}}]
  (some-> input :c :f :h inc))
user> 8

(let [input {:a 1 :b 2 :c {:d 4 :e 5 :f {:h 7}}}]
  (some-> input :c :z :h inc))

user> nil

(let [input {:a 1 :b 2 :c {:d 4 :e 5 :f {:h 7}}}]
  (-> input :c :z :h inc))

user> NullPointerException   clojure.lang.Numbers.ops (Numbers.Java:1013)

some->関数はOption [T]を提供しています。

2
Jon Seltzer