2011-11-19 6 views
13

W let postaci (Clojure tutaj) mogę robić coś jakDlaczego nie ma destructingu w formie def?

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

gdzie svd Zwraca listę długości trzy. Jest to bardzo naturalny porządek rzeczy do zrobienia, więc dlaczego nie jest to, że my nie mamy

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

i jej różnych uogólnień jak domyślne zachowanie formularza def? Nie widzę, jak mogłoby to kolidować z tym, co już robi def. Czy ktoś, kto rozumie Zen of Lisp lub Clojure, wyjaśnia dlaczego def nie obsługuje wiązania (z destrukturyzacją) tak potężnego jak let?

Odpowiedz

15

def to specjalna forma na poziomie kompilatora: tworzy ona Var. def musi być dostępny i możliwy do wykorzystania przed udostępnieniem destrukturyzacji. Zobaczysz coś podobnego z let*, prymitywem kompilatora, który nie obsługuje destrukturyzacji: następnie po kilku tysiącach wierszy w clojure/core.clj język jest na tyle potężny, aby zapewnić wersję let z destrukturyzacją, jako makro na szczycie let*.

Jeśli chcesz, możesz napisać makro (na przykład def+), które zrobi to za ciebie. Osobiście uważam, że jest trochę obrzydliwe i nie użyłoby go, ale używanie Lisp oznacza używanie języka, który pasuje do ciebie osobiście.

+1

myślę, że to jest to rodzaj odpowiedzi byłem zainteresowany. Na ryzyko nadmiernego editorialization z mojej strony, myślę, że mówisz, że powodem nie jest to zrobione w Clojure jest częściowo techniczny (w 'def' że dzieje się kompilator prymitywny), a częściowo przez konwencji (w tym jeden (np Rich Hickey) mógł rozpocząć z prymitywnym 'def' * i uznanej' def' później w pewnym momencie w rdzeniu). –

+1

@GabrielMitchell tak, to byłoby możliwe. Ale jest o wiele mniej użyteczne dla 'def' niż dla' let' i nie ma symetrii. 'let' ** always ** pobiera wektor i destruktury wewnątrz; wykonanie "def" czyni to znacznie mniej wygodnym, a sprawienie, by def przyjmowało albo symbol, albo formę destruktywną, jest dość okropnym IMO. – amalloy

+0

Czy możesz powiedzieć coś więcej o tym, dlaczego takie makro byłoby obrzydliwe? W tej chwili myślę, że to świetny pomysł, ale na podstawie twojego komentarza zastanawiam się, czy w jakiś sposób będzie to działało wbrew Clojure. Zwykle lepiej iść w zgodzie z językiem niż coś, co działa przeciwko niemu, ale wydaje się atrakcyjne dla kogoś niedoświadczonego w tym języku. Fakt, że po 10 latach korzystanie z takiego makra nie jest jeszcze powszechne, sprawia, że ​​zastanawiam się, czy jest to niezręczne rozwiązanie problemu najlepiej rozwiązanego w inny sposób. –

2

def jest w zasadzie konstruktorem dla Vars. Pierwszym argumentem jest symbol, który nazywa Var. To bierze ten symbol i zwraca Var dla tego symbolu. Przebudowa zmieniłaby te semantyki.

Można jednak napisać makro, które to robi.

+1

nie to, co powiedział pan równie prawdziwe w odniesieniu do 'let'? Chodzi mi o to, czy nie można by powiedzieć tego samego argumentu mówiącego, że 'let' nie powinien wspierać wiązania destrukturyzacyjnego? – sepp2k

+1

Tak, sepp2k porusza kwestię, o którą się zastanawiam. O ile rozumiem, podstawowa różnica między 'def' i' let' jest zakresem powiązań, a jednak 'let' wydaje się mieć bardziej ogólne zachowanie. Jak Chuck wskazuje na fakt, że można napisać makro, które jest polimorficzny w pierwszym argumencie (jeden zachowanie dla pojedynczego symbolu, inny zachowanie dla list symboli) sprawia, że ​​zastanawiam się, dlaczego nie jest to domyślna implementacja. –

0

To nie jest idealne, ale to rozpocznie się pisanie 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)]]))) 

z tym można zrobić ...

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

... jednocześnie nie naruszając prostotę def ...

(def+ foo "bar") 

.. , o co się prosiliśmy i zasugerowaliśmy. Problem ten nadal występuje przy wprowadzaniu zmiennych gensym do globalnej przestrzeni nazw. Problem z gensymem może zostać obsłużony, ale biorąc pod uwagę przypadek użycia (użyj w repl) dodatkowe zmienne są prawdopodobnie akceptowalne.

1

Poniżej znajduje się kilka filozoficznych uzasadnień.

Clojure sprzyja niezmienności nad zmiennością, a wszystkie źródła zmienności należy dokładnie rozważyć i nazwać. def tworzy zmienne vars. Idiomatic Clojure w związku z tym obaj nie używają ich zbyt wiele, a także nie chciałby zbyt łatwo tworzyć wiele zmiennych vars bez opieki (np. Przez destrukturyzację). let i destrukturywanie argumentów funkcji tworzy jednak niezmienne powiązania, dzięki czemu Clojure ułatwia tworzenie tych powiązań.

Vars stworzone przez def mieć zasięg globalny. Dlatego powinieneś starannie wymieniać nazwy def ed vars i zachować ich kilka. Destrukturyzacja def spowodowałaby, że zbyt łatwe byłoby utworzenie wielu plików def. let i rozpad argumentem funkcji, z drugiej strony, tworzy lokalne, leksykalnie o zakresie powiązań, więc wygoda destructuring nie powoduje zanieczyszczenia nazwy.