LINUX.ORG.RU

Racket и анафора

 ,


0

2

Я правильно понимаю, что реализация aif будет выглядеть как-то так:

(provide it aif)

(define it #f)

(define (set-it! val) (set! it val))

(define-syntax aif
  (syntax-rules ()
    [(aif cond if-true if-false)
     (begin (set-it! cond)
            (if it if-true if-false))]))

Или можно как-то красивее?

★★★★★

Неправильно:

(aif #t
     (begin (display it)
            (aif #f 'x 'y)
            (display it) )
     'z )
Анафорические макросы негигиеничны из-за своего it, поэтому syntax-rules их не выразить (с соблюдением лексических областей видимости, см. пример). Syntax-case же:
(define-syntax (aif x)
  (syntax-case x ()
    ((aif cond then else)
     (with-syntax ((it (datum->syntax x 'it)))
       (syntax
        (let ((it cond))
          (if it then else) ) ) ) ) ) )

ilammy ★★★
()
Ответ на: комментарий от ilammy

А, кстати, inb4

(let ([old-it it])
  (set-it! cond)
  (if it if-true if-false)
  (set-it! old-it) )
Вспомните про call/cc и прочие эксепшны. Можно, конечно, эти сеты завернуть в dynamic-wind, но...

ilammy ★★★
()
Ответ на: комментарий от qaqa

А по-моему фигня все эти анафорические формы. Если у нас есть две такие формы, одна внутри другой, то как нам во внутренней форме обратиться к внешнему it? Уж лучше явно задавать имя параметра.

korvin_ ★★★★★
()
Ответ на: комментарий от korvin_

Вот так?

;;#lang racket

(define-syntax aif/let 
  (λ (stx) 
    (syntax-case stx () 
      [(_ (id cond-exp) then-exp else-exp)
       #'(let ([id cond-exp]) 
           (if id 
               then-exp 
               else-exp))])))

(aif/let (it1 5)
  (aif/let (it2 6)
    (+ it1 it2)
    'else-expr2)
  'else-expr1)
qaqa ★★
()
Ответ на: Вот так? от qaqa

А почему не

(define-syntax aif
  (syntax-rules ()
    [(aif (it cond) if-true if-false)
     (let ([it cond])
       (if it if-true if-false))]))

?

monk ★★★★★
() автор топика
Ответ на: комментарий от monk

Кстати, а как в Racket или Scheme отлаживать макросы? В CL есть macroexpand и macroexpand-1 а здесь?

expand и expand-once соответственно. А еще кнопочка «Macro Stepper».

korvin_ ★★★★★
()
Ответ на: комментарий от korvin_

Спасибо. Только (syntax->datum (expand '(...))). А то он НЁХ вместо результата выводит (я поэтому при предыдущем поиске её и пропустил).

monk ★★★★★
() автор топика
Ответ на: комментарий от monk

В данном случае принципиальной разницы нет.

qaqa ★★
()
Ответ на: комментарий от korvin_

Посмотри на крокодила, например, (expand '(for/list ([i (range 1 10)]) (for/list ([j (range 1 10)]) (* i j)))). То, что он выдаёт после обработки syntax->datum хотя бы читать можно

monk ★★★★★
() автор топика
Ответ на: комментарий от korvin_

А из syntax'а даже код не скопировать.

monk ★★★★★
() автор топика

Да, да!!!

Анафорические лямбды, пандорические захваты, динамический ветер, мокролиты! Только хардкор!! Я такого треда джва года ждал.

anonymous
()
Ответ на: комментарий от monk

Посмотри на крокодила, например, (expand

А ты чего хотел? expand-once спасет тебя. Не забывай, что в отличие от CL macroexpand, Racket expand раскрывает вообще все макросы, а не только топовый.

А из syntax'а даже код не скопировать.

Во-первых скопировать http://www.youtube.com/watch?v=j5lx2GBVzGI , во-вторых результат макроэкспанда не всегда можно скопировать из-за gensym.

korvin_ ★★★★★
()
Ответ на: комментарий от korvin_

в отличие от CL macroexpand, Racket expand раскрывает вообще все макросы, а не только топовый.

macroexpand — тоже все. expand-once <=> macroexpand-1.

Во-первых скопировать

Я с середины пытался помечать. С хвоста действительно работает.

во-вторых результат макроэкспанда не всегда можно скопировать из-за gensym.

Скопировать можно всегда.

CL-USER> (macroexpand '(loop :for i :from 1 :to 10 :collect i))
(BLOCK NIL
  (LET ((I 1))
    (DECLARE (TYPE (AND REAL NUMBER) I))
    (SB-LOOP::WITH-LOOP-LIST-COLLECTION-HEAD (#:LOOP-LIST-HEAD-1148
                                              #:LOOP-LIST-TAIL-1149)
      (SB-LOOP::LOOP-BODY NIL
                          (NIL NIL (WHEN (> I '10) (GO SB-LOOP::END-LOOP)) NIL)
                          ((SB-LOOP::LOOP-COLLECT-RPLACD
                            (#:LOOP-LIST-HEAD-1148 #:LOOP-LIST-TAIL-1149)
                            (LIST I)))
                          (NIL (SB-LOOP::LOOP-REALLY-DESETQ I (1+ I))
                           (WHEN (> I '10) (GO SB-LOOP::END-LOOP)) NIL)
                          ((RETURN-FROM NIL
                             (SB-LOOP::LOOP-COLLECT-ANSWER
                              #:LOOP-LIST-HEAD-1148)))))))

С аналогичным Racket'овским хуже:

> (syntax->datum (expand '(for/list ([fold-var (in-range 1 10)]) fold-var)))

'(#%app
  alt-reverse
  (let-values (((start) '1) ((end) '10) ((inc) '1))
    (if (if (#%app real? start) (if (#%app real? end) (#%app real? inc) '#f) '#f) (#%app void) (let-values () (#%app in-range start end inc)))
    (#%app
     (letrec-values (((for-loop)
                      (lambda (fold-var pos)
                        (if (#%app unsafe-fx< pos end)
                          (let-values (((fold-var) pos))
                            (if '#t
                              (let-values (((fold-var)
                                            (let-values (((fold-var) fold-var)) (let-values () (#%app cons (let-values () fold-var) fold-var)))))
                                (if '#t (#%app for-loop fold-var (#%app unsafe-fx+ pos inc)) fold-var))
                              fold-var))
                          fold-var))))
       for-loop)
     null
     start)))

Как я должен догадаться, что в (#%app cons (let-values () fold-var) fold-var)) разные имена fold-var?

monk ★★★★★
() автор топика
Последнее исправление: monk (всего исправлений: 1)
Ответ на: комментарий от monk

macroexpand — тоже все.

Нет, ты не понял:

(defmacro begin (&body body)
  `(progn ,@body))

(macroexpand '(begin (begin (print 1))))
; => (PROGN (BEGIN (PRINT 1))) ; -- внутренний begin не раскрылся
(define-syntax-rule (progn body ...)
  (begin body ...))

(expand '(progn (progn (display 1))))
; => (begin (begin (#%app display '1))) ; -- все progn'ы раскрылись

Скопировать можно всегда.

Я к тому, что

(defmacro foo ()
  (let ((x (gensym)))
    `(eq ',x ',x)))

(foo)
; => T

(macroexpand '(foo))
; => (EQ '#:G744 '#:G744)
;; Copy ^ , Paste v

(EQ '#:G744 '#:G744)
; => NIL

Хотя я вообще не уловил смысл копирования макроекспанда.

korvin_ ★★★★★
()
Ответ на: комментарий от korvin_

Хотя я вообще не уловил смысл копирования макроекспанда.

1. Показать его на форуме и задать вопрос :-)

2. Убрать зависимость от библиотеки с макросами, если из неё нужен только один макрос.

monk ★★★★★
() автор топика
Ответ на: комментарий от korvin_

(EQ '#:G744 '#:G744)

Тут хотя бы явно видно, что не так (#: не пропустишь).

А в scheme:

(define x 'y)

(define-syntax-rule (foo a)
    (let ([x 'x])
      (eq? a x)))

(foo x) ; => #f

(expand '(foo x)) 
; => '(let-values (((x) 'x))
;       (#%app eq? x x))

(let-values (((x) 'x))
       (#%app eq? x x)) ; => #t

И угадать, что пошло не так, гораздо сложнее.

monk ★★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.