2011-11-19 42 views
13

Trong một hình thức let (Clojure đây) Tôi có thể làm một cái gì đó giống nhưTại sao không phá hủy ở dạng def?

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

nơi svd trả về một danh sách dài ba. Đây là một loại rất tự nhiên của việc cần làm, vậy tại sao không phải là chúng tôi không chúng tôi có

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

và khái quát khác nhau của nó như là hành vi mặc định của mẫu def? Tôi không thấy làm thế nào điều này sẽ can thiệp với bất cứ điều gì mà def đã làm. Ai đó có thể hiểu được Zen của Lisp hoặc Clojure giải thích tại sao def không hỗ trợ ràng buộc (với destructuring) mạnh mẽ như let?

Trả lời

15

def là một biểu mẫu đặc biệt ở cấp trình biên dịch: nó tạo một biến thể. def phải khả dụng và có thể sử dụng được trước khi việc hủy có sẵn. Bạn thấy một cái gì đó tương tự với let*, một trình biên dịch nguyên thủy hỗ trợ không phá hủy: sau đó sau hàng nghìn dòng trong clojure/core.clj ngôn ngữ cuối cùng đủ mạnh để cung cấp phiên bản let với destructuring, như macro trên đầu trang let*.

Nếu muốn, bạn có thể viết macro (ví dụ: def+) thực hiện điều này cho bạn. Cá nhân tôi nghĩ rằng đó là kinda tổng và sẽ không sử dụng nó, nhưng bằng cách sử dụng một Lisp có nghĩa là nhận được để sử dụng một ngôn ngữ phù hợp với bạn cá nhân.

+1

Tôi nghĩ rằng đây là loại câu trả lời mà tôi quan tâm. Nguy cơ biên tập quá mức về phía tôi, tôi đoán bạn đang nói rằng lý do không được thực hiện trong Clojure là một phần kỹ thuật (trong đó 'def' là một nguyên thủy của trình biên dịch), và một phần theo quy ước (trong đó một (ví dụ: Rich Hickey) có thể đã bắt đầu với một 'def' nguyên thủy và khai báo' def' sau đó tại một số điểm trong lõi). –

+1

@GabrielMitchell có, điều đó đã có thể thực hiện được. Nhưng nó ít hữu ích hơn đối với 'def' hơn là' let', và sẽ thiếu tính đối xứng. 'let' ** luôn luôn ** lấy một vector và các destructures bên trong đó; để làm cho 'def' làm như vậy sẽ làm cho nó ít thuận tiện hơn, và để làm cho def chấp nhận hoặc là một biểu tượng hoặc một hình thức destructuring là IMO khá khủng khiếp. – amalloy

+0

Bạn có thể nói thêm một chút về lý do tại sao một macro như vậy sẽ là tổng? Ngay bây giờ, tôi nghĩ rằng đó là một ý tưởng tuyệt vời - nhưng, dựa trên bình luận của bạn, tôi cũng tự hỏi liệu nó có bằng cách nào đó sẽ chạy ngược lại với hạt Clojure hay không. Thông thường thì tốt hơn là đi với hạt ngôn ngữ hơn là dựng lên thứ gì đó chạy ngược lại nhưng có vẻ hấp dẫn với một người không có kinh nghiệm với ngôn ngữ. Thực tế là sau 10 năm, việc sử dụng một vĩ mô như vậy không phải là phổ biến hơn nữa làm cho tôi tự hỏi nếu nó là một giải pháp vụng về cho một vấn đề tốt nhất giải quyết một cách khác. –

2

def về cơ bản là hàm tạo cho Vars. Đối số đầu tiên là ký hiệu có tên là Var. Nó lấy biểu tượng đó và trả về một Var cho biểu tượng đó. Destructuring sẽ thay đổi các ngữ nghĩa này.

Tuy nhiên, bạn có thể viết macro.

+1

Chẳng phải những gì bạn nói đều đúng với 'let'? Tôi có nghĩa là không thể lập luận tương tự để nói rằng 'let' không nên hỗ trợ ràng buộc phá hoại? – sepp2k

+1

Có, sepp2k mang đến điểm mà tôi tự hỏi. Cho đến khi tôi hiểu, sự khác biệt cơ bản giữa 'def' và' let' là phạm vi của các ràng buộc, và 'let' dường như có một hành vi tổng quát hơn. Như Chuck chỉ ra, thực tế là bạn có thể viết macro đa hình trong đối số đầu tiên (một hành vi cho một biểu tượng duy nhất, một hành vi khác cho danh sách biểu tượng) khiến tôi tự hỏi tại sao đây không phải là triển khai mặc định. –

0

Đây không phải là hoàn hảo nhưng nó là bắt đầu vào viết một 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)]]))) 

Với nó, bạn có thể làm ...

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

... trong khi không ảnh hưởng đến sự đơn giản của def ...

(def+ foo "bar") 

.. Đó là những gì đã được yêu cầu và đề xuất. Điều này vẫn có vấn đề giới thiệu các biến gensym vào không gian tên chung. Vấn đề về mật mã có thể được xử lý nhưng được đưa ra trường hợp sử dụng (sử dụng trong repl) các biến bổ sung có thể được chấp nhận.

1

Sau đây là một số giải thích triết học.

Clojure ủng hộ bất biến đối với tính đột biến và tất cả các nguồn có khả năng thay đổi nên được xem xét và đặt tên cẩn thận. def tạo các vars có thể thay đổi. Do đó, cả Clojure đều không sử dụng chúng nhiều, và cũng không muốn quá dễ dàng để tạo ra nhiều vars có thể thay đổi mà không cần quan tâm (ví dụ: bằng cách phá hủy). Tuy nhiên, let và chức năng phá hủy đối số, tạo ra các ràng buộc bất biến, vì vậy Clojure làm cho các ràng buộc đó dễ dàng tạo ra.

Vars được tạo bởi def có phạm vi toàn cầu. Do đó, bạn nên đặt tên cho cẩn thận def vars và giữ chúng ít về số lượng. Destructuring def sẽ giúp bạn dễ dàng tạo nhiều số def mà không cần quan tâm. Mặt khác, let và hàm đối số phá hủy tạo ra các liên kết cục bộ, từ xa, do đó sự tiện lợi của việc phá hủy không gây ra ô nhiễm tên.

Các vấn đề liên quan