В чем разница между define и define-синтаксисом и let-let-синтаксисом, когда значением является syntax-rules? - PullRequest
0 голосов
/ 11 апреля 2020

Я нахожусь в процессе реализации макросов Hygieni c в моей реализации Схемы, я только что реализовал syntax-rules, но у меня есть этот код:

(define odd?
  (syntax-rules ()
    ((_ x) (not (even? x)))))

какая разница должна быть между этим и этим:

(define-syntax odd?
  (syntax-rules ()
    ((_ x) (not (even? x)))))

из того, что я понимаю syntax-rules просто вернуть преобразователь синтаксиса, почему вы не можете просто использовать define, чтобы присвоить это символу? Почему мне нужно использовать define-syntax? Какие дополнительные функции выполняет это выражение?

Должно ли сначала работать в схеме? Или только второй?

Кроме того, в чем разница между let против let-syntax и letrec против letrec-syntax. Должен ли (define|let|letrec)-syntax просто проверить тип, если значение является синтаксическим преобразователем?

РЕДАКТИРОВАТЬ :

У меня есть эта реализация, все еще использующая макросы lisp:

;; -----------------------------------------------------------------------------
(define-macro (let-syntax vars . body)
  `(let ,vars
     ,@(map (lambda (rule)
              `(typecheck "let-syntax" ,(car rule) "syntax"))
            vars)
     ,@body))

;; -----------------------------------------------------------------------------
(define-macro (letrec-syntax vars . body)
  `(letrec ,vars
     ,@(map (lambda (rule)
              `(typecheck "letrec-syntax" ,(car rule) "syntax"))
            vars)
     ,@body))

;; -----------------------------------------------------------------------------
(define-macro (define-syntax name expr)
  (let ((expr-name (gensym)))
    `(define ,name
       (let ((,expr-name ,expr))
         (typecheck "define-syntax" ,expr-name "syntax")
         ,expr-name))))

Этот код правильный?

Должен ли этот код работать?

(let ((let (lambda (x) x)))
  (let-syntax ((odd? (syntax-rules ()
                        ((_ x) (not (even? x))))))
     (odd? 11)))

1 Ответ

2 голосов
/ 12 апреля 2020

Этот вопрос, по-видимому, подразумевает глубокую путаницу в отношении макросов.

Давайте представим язык, в котором syntax-rules возвращает некоторую функцию преобразования синтаксиса (я не уверен, что это должно быть верно в схеме RnRS, это правда в Racket, я думаю), и где let и let-syntax были одинаковыми.

Итак, давайте напишем эту функцию:

(define (f v)
  (let ([g v])
    (g e (i 10)
       (if (= i 0)
           i
           (e (- i 1))))))

Что мы можем превратить в это, конечно:

(define (f v n)
  (v e (i n)
     (if (<= i 0)
         i
         (e (- i 1)))))

И, кроме того, я скажу вам, что в среде не существует привязки для e или i.

Что интерпретатор должен делать с этим определением? Это могло скомпилировать это? Можно ли с уверенностью заключить, что i не может иметь никакого смысла, поскольку он используется как функция, а затем как число? Может ли он вообще что-нибудь сделать безопасно?

Ответ - нет, не может. Пока он не знает, какой аргумент у функции, он ничего не может сделать. А это значит, что каждый раз, когда вызывается f, он должен снова принять это решение . В частности, v может быть:

(syntax-rules ()
  [(_ name (var init) form ...)
   (letrec ([name (λ (var)
                    form ...)])
     (name init))]))

, при котором определение f имеет некоторый смысл.

И все становится хуже: гораздо хуже. Как насчет этого?

(define (f v1 v2 n)
  (let ([v v1])
    (v e (i n)
       ...
       (set! v (if (eq? v v1) v2 v1))
       ...)))

Что это означает, что подобная система не будет знать, что означает код, который она должна интерпретировать, до того момента, пока она ее не интерпретирует, или даже после эта точка , как вы можете видеть из второй функции выше.

Так что вместо этого ужаса Лисп делает что-то вменяемое: они делят процесс вычисления битов кода на фазы где каждая фаза происходит концептуально перед следующей.

Вот последовательность для некоторого воображаемого Лиспа (это отчасти похоже на то, что делает CL, поскольку большая часть моих знаний об этом, но она не предназначена для представления какой-либо конкретной системы):

  1. есть фаза, когда код превращается из некоторой последовательности символов в некоторый объект, возможно, с помощью пользовательского кода;
  2. есть фаза, на которой этот объект переписывается в какой-то другой объект с помощью пользовательского и системного кода (макросов) - результатом этой фазы является то, что является явным d с точки зрения функций и некоторого небольшого числа примитивных специальных вещей, традиционно называемых «специальными формами», которые известны процессам стадий 3 и 4;
  3. может быть фаза, в которой объект из фазы 2 компилируется, и эта фаза может включать другой набор пользовательских макросов (макросы компилятора);
  4. есть фаза, в которой оценивается результирующий код.

И для каждой единицы кодируют эти фазы в порядке , каждая фаза завершается до начала следующей .

Это означает, что каждая фаза, в которую может вмешаться пользователь, нуждается в своем собственном наборе определение и связывание форм: необходимо иметь возможность сказать, что «эта вещь контролирует, например, то, что происходит на этапе 2».

Вот что делают define-syntax, let-syntax & c: они говорят, что «эти привязки и определения управляют тем, что происходит на этапе 2». Вы не можете, например, использовать define или let, чтобы сделать это, потому что на этапе 2, эти операции еще не имеют значения : они приобретают значение (возможно, сами по себе являются макросами, которые расширить до некоторой примитивной вещи) только на этапе 3. На этапе 2 это всего лишь фрагменты синтаксиса, которые макрос принимает и выплевывает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...