<div>Hi Nick,</div><div><br></div>2013/8/4 Nick Sivo <span dir="ltr"><<a href="mailto:nicksivo@gmail.com" target="_blank">nicksivo@gmail.com</a>></span><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
I'd like to make Arc work as a Racket language (and in the process<br>
preserve srclocs and names), and have made some progress[4]. However,<br>
I'm hitting a wall trying to implement Arc's macros. Specifically, I<br>
need to find a way for the macros themselves to use previously defined<br>
Arc functions as part of their processing. I understand that there are<br>
good reasons for Racket's hygiene and syntax phases, but I'm<br>
implementing an existing language that has its own strong opinions.<br>
I'm almost certain that accomplishing my goal is possible with the<br>
right understanding, but despite reading much of the Racket Reference,<br>
I don't truly get it yet.<br></blockquote><div><br></div><div>The evaluation model of Arc makes it hard to device a general solution. The problem is that arc-identifiers are represented</div><div>as symbols which do not contain any source location information.</div>
<div><br></div><div>For a macro like this:</div><div><pre style="word-wrap:break-word;white-space:pre-wrap"> (mac when (test . body)
(list 'if test (cons 'do body)))</pre><pre style="word-wrap:break-word;white-space:pre-wrap"><span style="font-family:arial;white-space:normal">which defines a when macro that expands </span></pre><pre style="word-wrap:break-word;white-space:pre-wrap">
<span style="font-family:arial;white-space:normal"> (when test body ...) to (if test (do body ...))</span></pre><pre style="word-wrap:break-word;white-space:pre-wrap"><span style="font-family:arial">you know the source location of the identifier when.</span></pre>
<pre style="word-wrap:break-word;white-space:pre-wrap"><br></pre><pre style="word-wrap:break-word;white-space:pre-wrap"><font face="arial">However if you have a arc-macro that expands to into a new</font></pre><pre style="word-wrap:break-word;white-space:pre-wrap">
<font face="arial">arc-macro definition, the arc programmer has no way of</font></pre><pre style="word-wrap:break-word;white-space:pre-wrap"><font face="arial">attaching source location information to the generated</font></pre>
<pre style="word-wrap:break-word;white-space:pre-wrap"><font face="arial">arc-identifer.</font></pre><pre style="word-wrap:break-word;white-space:pre-wrap"><font face="arial"><br></font></pre><pre style="word-wrap:break-word;white-space:pre-wrap">
<font face="arial">That said an 80% solution is better than nothing.</font></pre><pre style="word-wrap:break-word;white-space:pre-wrap"><span style="font-family:arial"> </span></pre></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
With that motivation in mind, what's the best way for me to become<br>
intimately familiar with Racket's runtime, execution and compilation<br>
model? Should I read through and make sure I understand the debugger?<br>
The macro stepper? Is there any kind of guide to Racket Internals?<br></blockquote><div><br></div><div>I would read up on modules (and submodules) and experiment</div><div>with define-syntax and begin-for-syntax. Skip the debugger.</div>
<div><br></div><div>Given the way Arc mixes the phases it is hard to compile Arc to anything. </div><div><div>Consider for example how you would compile Arc-with-macros </div><div>into Arc-without-macros. </div></div><div>
<br></div><div>If I understand the evaluation model correctly, then you basically can't</div><div>compile an Arc program without running it. If you assume that certain</div><div>names aren't redefined (such as if, mac and others) then it might</div>
<div>be possible to compile parts.</div><div><br></div><div>> Specifically, I need to find a way for the macros themselves to use </div><div>> previously defined Arc functions as part of their processing. </div><div>
<br></div><div>Here is a few examples that illustrate how to make a function</div><div>available both in runtime (phase 0) and at compile time (here in phase 1).</div><div><br></div><div>The idea of the final example is to compile (read arc-definitions) definitions</div>
<div>into submodule, which are then required in both phases. This implies</div><div>that the code is run twice...</div><div><br></div><div>/Jens Axel Søgaard</div><div><br></div><div><br></div><div><div>#lang racket</div>
<div><br></div><div>; Let us define a simple macro myand and use it as</div><div>; a running example.</div><div><br></div><div>(module ex1 racket</div><div> (define-syntax (myand stx)</div><div> (syntax-case stx ()</div>
<div> [(_) #'#t]</div><div> [(_ expr0 expr ...) #'(if expr0 (myand expr ...) #f)]))</div><div> </div><div> ; Test it:</div><div> (displayln "Example 1")</div><div> (myand (= 1 1) (= 2 3)) ; => #f</div>
<div> (myand (= 1 1) (= 2 2)) ; => #t</div><div> ; Conclusion: It works on runtime.</div><div> )</div><div><br></div><div>; Now can we use in a macro (at expansion time?)</div><div><br></div><div>#;(module ex2 racket</div>
<div> (define-syntax (myand stx)</div><div> (syntax-case stx ()</div><div> [(_) #'#t]</div><div> [(_ expr0 expr ...) #'(if expr0 (myand expr ...) #f)]))</div><div> </div><div>
(define-syntax (macro-that-uses-myand stx)</div><div> (syntax-case stx ()</div><div> [(_ x y z) </div><div> (if (myand (= 1 1)) ; <= this myand gives the error below</div><div> #''one-equals-one</div>
<div> #''huh?)])))</div><div><br></div><div>; Nope. The example ex2 gives the error:</div><div>; myand: unbound identifier in module (in the transformer environment, </div><div>; which does not include the macro definition that is visible to run-time </div>
<div>; expressions) in: myand</div><div><br></div><div>; In order to make it visible, we need to define it both in the runtime</div><div>; and transformer environment.</div><div><br></div><div>(module ex3 racket</div>
<div> (define-syntax (myand stx)</div><div> (syntax-case stx ()</div><div> [(_) #'#t]</div><div> [(_ expr0 expr ...) #'(if expr0 (myand expr ...) #f)]))</div><div> (begin-for-syntax</div>
<div> (require (for-syntax racket))</div><div> (define-syntax (myand stx)</div><div> (syntax-case stx ()</div><div> [(_) #'#t]</div><div> [(_ expr0 expr ...) #'(if expr0 (myand expr ...) #f)])))</div>
<div> (define-syntax (macro-that-uses-myand stx)</div><div> (syntax-case stx ()</div><div> [(_ x y z) </div><div> (if (myand (= 1 1))</div><div> #''one-equals-one</div><div> #''huh?)])) </div>
<div> (displayln "Example 3") </div><div> (macro-that-uses-myand 1 2 3)</div><div> (myand (= 1 1) (= 2 3)))</div><div><br></div><div>; Writing the same macro twice is a bad idea, so we'll let</div><div>; a macro do the job.</div>
<div><br></div><div>(module define*-mod racket</div><div> (provide define*)</div><div><br></div><div> (define-syntax (define* stx)</div><div> (syntax-case stx ()</div><div> [(_ macro-definition)</div><div> #'(begin</div>
<div> (begin-for-syntax macro-definition)</div><div> macro-definition)])))</div><div><br></div><div>; Let's test the macro:</div><div><br></div><div>(module ex4 racket</div><div> (require (submod ".." define*-mod)</div>
<div> (for-syntax (for-syntax racket))) ; make syntax-case available in phase 2</div><div> (define*</div><div> (define-syntax (myand stx)</div><div> (syntax-case stx ()</div><div> [(_) #'#t]</div>
<div> [(_ expr0 expr ...) #'(if expr0 (myand expr ...) #f)])))</div><div> (define-syntax (macro-that-uses-myand stx)</div><div> (syntax-case stx ()</div><div> [(_ x y z) </div><div> (if (myand (= 1 1))</div>
<div> #''one-equals-one</div><div> #''huh?)])) </div><div> (displayln "Example 4")</div><div> (macro-that-uses-myand 1 2 3)</div><div> (myand (= 1 1) (= 2 3)))</div><div><br>
</div><div>; This works, but in general code duplication is a Bad Thing (TM).</div><div>; If we place the definition of myand in a module, we can import it</div><div>; in both phases.</div><div><br></div><div>(module ex5 racket</div>
<div> ; A submodule containing a definition of myand:</div><div> (module myand-mod racket</div><div> (provide myand)</div><div> (define-syntax (myand stx)</div><div> (syntax-case stx ()</div><div> [(_) #'#t]</div>
<div> [(_ expr0 expr ...) #'(if expr0 (myand expr ...) #f)])))</div><div> </div><div> ; Import it in both phases:</div><div> (require (submod "." myand-mod)</div><div> (for-syntax (submod "." myand-mod)))</div>
<div> ; Test it:</div><div> (define-syntax (macro-that-uses-myand stx)</div><div> (syntax-case stx ()</div><div> [(_ x y z) </div><div> (if (myand (= 1 1))</div><div> #''one-equals-one</div>
<div> #''huh?)])) </div><div> (displayln "Example 5")</div><div> (macro-that-uses-myand 1 2 3)</div><div> (myand (= 1 1) (= 2 3)))</div><div><br></div><div>; Now let's write a macro that can generate the submodule automatically.</div>
<div><br></div><div>(module define/mod-mod racket</div><div> (provide define/mod)</div><div> (define-syntax (define/mod stx)</div><div> (syntax-case stx ()</div><div> [(_ name definition)</div><div> #'(begin</div>
<div> (module name racket</div><div> (provide name)</div><div> definition)</div><div> #;(require (submod "." name) (for-syntax (submod "." name))) ; footnote *</div>
<div> )])))</div><div><br></div><div>(module ex6 racket</div><div> (require (submod ".." define/mod-mod))</div><div> (define/mod myand </div><div> (define-syntax (myand stx)</div><div> (syntax-case stx ()</div>
<div> [(_) #'#t]</div><div> [(_ expr0 expr ...) #'(if expr0 (myand expr ...) #f)])))</div><div> (require (submod "." myand) (for-syntax (submod "." myand))) ; footnote **</div>
<div> ; Test it:</div><div> (define-syntax (macro-that-uses-myand stx)</div><div> (syntax-case stx ()</div><div> [(_ x y z) </div><div> (if (myand (= 1 1))</div><div> #''one-equals-one</div>
<div> #''huh?)])) </div><div> (displayln "Example 6")</div><div> (macro-that-uses-myand 1 2 3)</div><div> (myand (= 1 1) (= 2 3)))</div><div><br></div><div>; Ideally the line marked ** could be commented out and the one marked * be used instead.</div>
<div>; If I remember correctly I need to do *something* with the require, but what ...</div><div><br></div><div>(require 'ex1)</div><div>(require 'ex3)</div><div>(require 'ex4)</div><div>(require 'ex5)</div>
<div>(require 'ex6)</div><div><br></div></div><div><br></div><div><br></div><div><br></div><div><br></div></div>