2017-05-15 61 views
6

Uczę się makr Clojure i zastanawiam się, dlaczego nie możemy używać prostych funkcji do metaprogramowania.Clojure, może makra zrobić coś, czego nie da się zrobić przy pomocy funkcji

O ile wiem, różnica między makro i funkcji jest to, że argumenty makra nie są oceniane, ale przekazywane jako struktur danych i symboli, ponieważ są one, podczas gdy wartości zwracanej jest oceniano (w miejscu, w którym makro jest nazywany). Makro działa jako pośrednik między czytnikiem a oceniającym, przekształcając formularz w sposób arbitralny przed dokonaniem oceny. Wewnętrznie mogą używać wszystkich funkcji językowych, w tym funkcji, specjalnych formularzy, literałów, rekursji, innych makr itp.

Funkcje są odwrotne. Argumenty są sprawdzane przed wywołaniem, zwracana wartość nie jest po powrocie. Ale lustrzana natura makr i funkcji sprawia, że ​​zastanawiam się, czy nie moglibyśmy równie dobrze użyć makr funkcji, cytując ich argumenty (formę), przekształcając formę, oceniając ją wewnątrz funkcji, ostatecznie zwracając jej wartość. Czy to logicznie nie dałoby takiego samego rezultatu? Oczywiście byłoby to niewygodne, ale teoretycznie, jest odpowiednikiem dla każdego możliwego makro?

Oto prosty infix makro

(defmacro infix 
    "translate infix notation to clojure form" 
    [form] 
    (list (second form) (first form) (last form))) 

(infix (6 + 6)) ;-> 12 

Tutaj jest sama logika użyciu funkcji

(defn infix-fn 
    "infix using a function" 
    [form] 
    ((eval (second form)) (eval (first form)) (eval (last form)))) 

(infix-fn '(6 + 6)) ;-> 12 

Teraz jest to postrzeganie uogólnić do wszystkich sytuacji, czy istnieją pewne przypadki narożne gdzie makro mogłem” t być przesadzone? Na koniec, czy makra są po prostu syntaktycznym cukrem nad wywołaniem funkcji?

+3

Zwróć uwagę, że makra są rekursywnie rozwijane w czasie makro wyskalowania (zwykle przed kompilacją), a nie podczas wykonywania. Skompilowany kod będzie wyglądał tak, jakbyś pisał ekspansję ręcznie, więc nie ma dla niego kary za wydajność. Pamiętaj również, że 'eval' ocenia formularz w pustym środowisku leksykalnym. '(niech [x 10] (infix-fn '(x + 6)))' => 'CompilerException ... Nie można rozwiązać symbolu: x' – jkiiski

+0

To są dobre punkty Nie myślałem o –

Odpowiedz

11

To pomoże, jeśli przeczytam pytanie przed udzieleniem odpowiedzi.

Twój Wrostek funkcja nie działa z wyjątkiem literałów:

(let [m 3, n 22] (infix-fn '(m + n))) 
CompilerException java.lang.RuntimeException: 
Unable to resolve symbol: m in this context ... 

Jest to konsekwencja tego, co @jkinski zauważył: do czasu eval akty, m znika.


makra można zrobić, jakie funkcje nie mogą?

Tak. Ale jeśli możesz zrobić to za pomocą funkcji, powinieneś.

Makra są dobre dla

  • oceny odroczone;
  • Przechwytywanie formularzy;
  • reorganizacja składni;

żadna z nich nie może wykonać żadnej funkcji.

(defn fact [n] 
    (if-like (pos? n) 
    (* (fact (dec n)) n) 
    1)) 

(map fact (range 10)) 
=> (1 1 2 6 24 120 720 5040 40320 362880) 

... to działa, mniej więcej.


Proszę, drogi czytelniku, wskaż błędy w moim kodzie.

+0

Leniwe oceny w przykład można również zrobić, przekazując następnie jako wyrażenie lambda do funkcji except. Czy są przypadki, w których nawet lambda tego nie zrobi? –

+0

@TuomasToivonen Myślę, że lambdas zawsze może to zrobić. Jak wiadomo, funkcja, której nie można odroczyć, jest oceną jej argumentów. Z pewnością zawsze możesz podać argumenty jako nieocenione formy przechwycone w lambdach. Możemy nawet użyć lambdas, aby utworzyć wygląd "if" jako makro: look, ma, no special form. Tak naprawdę zrobię to teraz :). – Thumbnail