2011-11-19 7 views
13

(Clojure에 여기에) 내가 svd 길이 세의 목록을 반환왜 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의 Zen을 이해하는 사람이 def이 (왜곡이있는) 바인딩을 let처럼 강력하게 지원하지 않는 이유를 설명 할 수 있습니까?

답변

15

def은 컴파일러 수준에서 특수한 형식입니다. Var가됩니다. def은 폐기가 가능하기 전에 사용 가능해야하며 사용 가능해야합니다. let*과 비슷한 것을 볼 수 있습니다. 즉, 아무런 구조화도 지원하지 않는 컴파일러 프리미티브가 있습니다. clojure/core.clj에서 수천 줄이 지난 후에 언어는 마침내 let* 위의 매크로로 구조가 해제 된 let 버전을 제공 할 정도로 강력합니다.

원하는 경우이 작업을 수행하는 매크로 (예 : def+)를 작성할 수 있습니다. 개인적으로 나는 다소 심한 것이고 그것을 사용하지 않을 것이라고 생각하지만, Lisp을 사용한다는 것은 당신에게 개인적으로 적합한 언어를 사용한다는 것을 의미합니다.

+1

나는 이것이 내가 관심을 갖고 있었던 일종의 대답이라고 생각한다. 내 편에 과도한 편집의 위험이 있음을 나는 Clojure에서하지 않는 이유가 부분적으로 기술적 인 것이라고 말하고 있다고 생각한다. 컴파일러 프리미티브), 부분적으로는 (Rich Hickey와 같은) 원시적 인'def' *로 시작할 수 있었고 나중에 코어의 어느 시점에서'def'를 선언 할 수있었습니다. –

+1

@ GabrielMitchell 예, 가능했을 것입니다. 그러나'def'는'let'보다 유용하지 않으며 대칭성이 부족합니다. 'let' ** 항상 ** 벡터 내부를 파기하고 파괴합니다. 'def'를 그렇게 만들면 훨씬 편리하지 않게되고, def가 심볼이나 파괴적인 형태를 받아들이도록 만드는 것은 꽤 끔찍합니다. – amalloy

+0

그런 매크로가 총체가되는 이유에 대해 좀 더 자세히 말씀해 주시겠습니까? 지금 당장은 좋은 생각이라고 생각합니다. 그러나 귀하의 의견을 토대로 Clojure의 곡식에 대해 어떻게 든 실행되는지 궁금합니다. 일반적으로 언어를 사용하는 것보다는 언어를 사용하는 것이 더 좋지만 언어에 익숙하지 않은 사람에게는 호소력이있는 것 같습니다. 10 년 후에 그런 매크로를 사용하는 것이 평범하지 않다는 사실은 내가 다른 방법으로 가장 잘 해결 된 문제에 대한 서투른 해결책인지 궁금하게 만든다. –

2

def은 기본적으로 Vars의 생성자입니다. 첫 번째 인수는 Var의 이름을 나타내는 기호입니다. 그것은 그 심볼을 취해 그 심볼에 대한 Var를 반환합니다. 파괴는 이러한 의미를 변화시킬 것이다.

그래도 매크로를 작성할 수 있습니다.

+1

당신이 말한 것이'let '과 똑같은 진실이 아니십니까? 제 말은 '하자'가 구속력있는 결합을지지해서는 안된다고 말하는 것과 같은 주장을 할 수는 없습니까? – sepp2k

+1

예, 제가 궁금해하는 점이 sepp2k에 있습니다. 내가 이해하는 한,'def'와'let'의 근본적인 차이는 바인딩의 범위이며,'let'은 좀 더 일반적인 행동을하는 것처럼 보입니다. Chuck이 지적했듯이 첫 번째 인수에서 다형성 인 매크로 (단일 심볼에 대한 하나의 동작, 심볼 목록에 대한 또 다른 동작)를 작성하면 이것이 기본 구현이 아닌 이유가 궁금합니다. –

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는 이러한 바인딩을 쉽게 만들 수 있습니다.

바가 def으로 생성되면 전역 범위가 적용됩니다. 따라서 신중하게 이름을 def으로 지정하고 숫자를 적게 유지해야합니다. def을 파괴하면 수많은 def을 쉽게 생성 할 수 있습니다. let 및 함수 인수 소멸은 지역적, 어휘 적 범위 바인딩을 작성하므로 구조화의 편리 성은 이름 오염을 일으키지 않습니다.