This repository has been archived by the owner on Aug 10, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
17.01-More_Macros
126 lines (109 loc) · 4.15 KB
/
17.01-More_Macros
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
MACROS
========
Na aula passada, vimos uma "confusão" dos macros por causa do
environment. A diferenciação entre ambos pode ser desejável, para que
possamos fazer definições de macros RECURSIVAS ou com AUTORREFERÊNCIA.
Os macros que evitam esse problema é um MACRO HIGIÊNICO, mas teria esse
novo problema. A implementação que daria este problema seria:
; Definição de um objeto com autorreferência
; usando a criação via macros.
(define os-1
(object/self-1
; Este self dará um ERRO. O processador de macros do Racket
; é higiênico. Logo, dentro do macro ele olhará se esse self
; foi definido NESTE AMBIENTE. Ele não foi: logo, aparecerá o
; erro do self não ser definido.
[first (x) (msg self 'second (+ x 1))]
[second (x) (+ x 1)]
))
; Como fazemos isso com macros agora?
(define-syntax object/self-1
(syntax-rules ()
[(object [mtd-name (var) val] ...)
; Primeiro, associamos self com um símbolo qualquer.
; No caso, é um lambda que imprime que está vazio.
(let ([self (lambda (msg-name)
(lambda (x) (error 'object "nothing here")))])
; Reassocia self com o lambda seletor dos métodos
(begin
(set! self
(lambda (msg)
(case msg
; O macro cria, um lambda usando o nome do
; método, a variável do lambda e o corpo.
; Como pode haver vários métodos, estamos
; fazendo de forma recursiva
[(mtd-name) (lambda (var) val)]
...)))
self)
) ;end let
]))
; O resultado do código expandido seria:
(let ([self 'dummy])
(begin
(set! self
(λ (msg)
(case msg
[(first (λ (x) (msg self)) )]
[(second (λ (x) (+ x 1)) )]
)))))
Para contornarmos esse problema, poderíamos passar o 'self' como
argumento para a macro. Dessa maneira, o símbolo ao ser usado dentro
do contexto do uso da macro, quando ela for setada.
; Por que esse self não dá problemas quando passado como argumento?
(define os-2
(object/self-2 self ; O nome do próprio argumento deve ser passado
; como primeiro parâmetro.
[first (x) (msg self 'second (+ x 1))]
[second (x) (+ x 1)]
))
(define-syntax object/self-2
(syntax-rules ()
[(object self [mtd-name (var) val] ...)
; Primeiro, associamos self com um símbolo qualquer.
; No caso, é um lambda que imprime que está vazio.
(let ([self (lambda (msg-name)
(lambda (x) (error 'object "nothing here")))])
; Reassocia self com o lambda seletor dos métodos. Agora,
; este é o self externo, e estamos afetando a tabela dos
; argumentos.
(begin
(set! self
(lambda (msg)
(case msg
[(mtd-name) (lambda (var) val)]
...)))
self)
) ;end let
]))
Mas nem sempre seria desejável passar 'self' como argumento. Como evitar
isso? Poderíamos usar um recurso adicional, suportado pelo Racket, que
permite associar um símbolo DENTRO DA SINTAXE com um DE FORA DA SINTAXE.
Dessa maneira, uma mudança de estado dentro da macro altera o espaço das
variáveis passadas como argumento para a macro. Assim, conseguiríamos
sobrescrever o self (o que não acontecia na 1ª forma, pois o self estava
em outra tabela de símbolos).
(define os-3
(object/self-3
[first (x) (msg self 'second (+ x 1))]
[second (x) (+ x 1)]
))
(define-syntax object/self-3
(syntax-case ()
[(object [mtd-name (var) val] ...)
(with-syntax ([self (datum->syntax x 'self)])
#'(let ([self (lambda (msg-name)
(lambda (x) (error 'object "nothing here")))])
(begin
(set! self
(lambda (msg-name)
(case msg-name
[(mtd-name) (lambda (var) val)]
...
)))
self
)
) ; let
) ; with-syntax
] ; definição de objeto
))