2011-11-19 10 views
13

(Clojureはこちら)私はsvdは長さ3のリストを返すなぜdef形式で破壊しないのですか? <code>let</code>形で

(let [[u s v] (svd A)] 
    (do-something-with u v)) 

のような何かをすることができます。これは、実行することの非常に自然なものですので、なぜ我々は

(def [u s v] (svd A)) 

defフォームのデフォルトの動作として、その様々な一般化を持っていないことではないでしょうか?私はこれがdefがすでに行っていることをどのように妨害するのか分かりません。 LispやClojureの禅を理解している人が、defが(破壊を伴う)バインディングをletとして強力にサポートしていない理由を説明できますか?

答えて

15

defは、コンパイラレベルで特別な形式です。これはVarを作成します。 defは利用可能であり、破壊が利用可能になる前に使用可能でなければなりません。 let*のようなものがあります。これは、非構造化をサポートしていないコンパイラプリミティブです。clojure/core.cljの数千行後には、let*の上のマクロとして、letのバージョンを破壊するのに十分なほど強力です。

必要に応じて、これを行うマクロ(たとえば、def+)を書くことができます。私は個人的にはそれほど重く、それを使用しないと思っていますが、Lispを使うということはあなたに個人的に適した言語を使うことを意味します。

+1

私はこれが私が興味を持った回答の一種だと思っています。私は過度の編集の危険がありますが、Clojureで行われていない理由は部分的には技術的なことですコンパイラプリミティブ)、そして部分的には慣習的に(リッチヒッキーなど)、プリミティブ 'def' *で開始し、後でコアのある時点で' def'を宣言することができます。 –

+1

@GabrielMitchellはい、これは可能でした。しかし、 '' let''より '' def''にはあまり役に立たず、対称性が欠けています。 'let' **常に**はベクトルをとり、その内部を破壊します。 'def'をするとそれほど便利ではないでしょうし、defにシンボルや構造化形式を受け入れるようにするのはかなりひどいIMOです。 – amalloy

+0

あなたはなぜそのようなマクロが総体であるのかについてもう少し言えますか?今、私はそれが素晴らしいアイデアだと思っていますが、あなたのコメントに基づいて、Clojureの穀物にどういうふうに実行するのかと思っています。通常は、それに反して実行するものよりも、言語の粒と一緒に行く方が良いですが、言語に慣れていない人には魅力的なようです。このようなマクロの使用が10年後には一般的ではないという事実は、別の方法で最良に解決された問題に対する不器用な解決策であるかどうか疑問に思う。 –

2

defは、基本的にVarsのコンストラクタです。最初の引数は、Varの名前を表すシンボルです。そのシンボルを受け取り、そのシンボルのVarを返します。破壊はこれらのセマンティクスを変えるでしょう。

あなたはそれを行うマクロを書くこともできます。

+1

「let」と同じように言ったことではありませんか?私は、 'let'が破壊を救済してはならないと言って同じ議論をすることはできないのでしょうか? – sepp2k

+1

はい、sepp2kは、私が不思議に思うことを述べています。私が理解する限り、 'def'と' let'の基本的な違いはバインディングの範囲ですが、 'let'はもっと一般的な振る舞いをするようです。 Chuckが指摘しているように、最初の引数(1つのシンボルに対して1つの動作、シンボルのリストに対して別の動作)で多態的なマクロを書くことができるということは、なぜこれがデフォルトの実装ではないのか不思議です。 –

0

...これは完璧ではないが、それはあなたが行うことができるとdef+ https://clojuredocs.org/clojure.core/destructure

(defmacro def+ 
    "binding => binding-form 
    internalizes binding-forms as if by def." 
    {:added "1.9", :special-form true, :forms '[(def+ [bindings*])]} 
    [& bindings] 
    (let [bings (partition 2 (destructure bindings))] 
    (sequence cat 
     ['(do) 
     (map (fn [[var value]] `(def ~var ~value)) bings) 
     [(mapv (fn [[var _]] (str var)) bings)]]))) 

を書く上で開始され

(def+ [u s v] [1 5 9], foo "bar") 

... defのシンプルさを損なうことなく...

(def+ foo "bar") 

..それは要求され提案されたものです。 グローバルな名前空間に のgensym変数を導入することには依然として問題があります。 gensymの問題を処理できましたが、 ユースケース(replで使用)を指定すると、追加の変数 がおそらく許容されます。

1

以下は、いくつかの哲学的正当化です。

Clojureは、変更可能性よりも不変性を優先し、すべての変更元を注意深く考慮して指定する必要があります。 defは変更可能な変数を作成します。したがって、慣用的なClojureは、とにかくそれらをあまり使用しませんし、多くの変更可能な変数を気にせずに(例えば、非構造化によって)作成することも容易ではないでしょう。しかし、letと関数の引数の破壊は、不変のバインディングを作成するので、Clojureはそれらのバインディングを簡単に作成します。

Varsによって作成されたdefは、グローバルスコープを持ちます。したがって、defの名前を慎重に付け、数を少なくしてください。 defを破棄すると、多くのものを作成するのが非常に簡単になります。def s。一方で、関数の引数の構造化を解除すると、局所的なレキシカルスコープのバインディングが作成されるため、構造の簡略化は名前の汚染を引き起こさない。

関連する問題