[racket] Macro + function automation

From: Carl Eastlund (cce at ccs.neu.edu)
Date: Thu Jul 11 02:45:44 EDT 2013

On Thu, Jul 11, 2013 at 1:49 AM, Roman Klochkov <kalimehtar at mail.ru> wrote:

> Thank you. You answer is very useful.
> > You have to be careful about duplicating inputs in expanded code.
> I thought, that hygiene implies not only cl:gensym, but also once-only. Is
> there something like
> http://common-lisp.net/project/cl-utilities/doc/once-only.html in Racket?

Hygiene is about the scope of names, and has nothing to do with duplicate
expressions.  Neither does once-only.  What once-only means, is that the
contained, single expression is not called multiple times at run-time.
What your macro did is not to evaluate a single expression twice, but
instead to construct _two_ separate expressions.  Each was still only
executed once.  Hygiene makes no guarantee about copying expressions;
however, hygiene does give macro implementers better tools for _avoiding_
copying, because it's easier and safer to bind values to so-called
"temporary" variables without unintended scope violation.

In a case where you do have one expression, and want it to be executed
once, the macro called `begin-lifted` from the module mzlib/etc is the
equivalent of once-only.  But that's not the issue in your square macro.

> > A lot of the benefits you get from explicit optimization will also come
> from using define-inline from racket/performance-hint.
> Do you think, that Racket will do pure calculations in compile-time?
> For example, if I write
> (+ x (* 2 3 4) (sin 20))
> will it be optimized to
> (+ x  24.912945250727628) ?
> Or I have to use define-syntax to do it?

Racket will do many pure calculations at compile-time, without any work on
your part.  I don't know exactly what it will and won't do.  I know the
Typed Racket optimization coach will help you make use of the extra
optimizations that Typed Racket performs, but I don't know what it tells
you about the core Racket optimizer.

In general, I suggest writing your code in the most natural style first.
If you find that it runs too slow, then start thinking about where you
could add manual optimizations.  It will almost certainly not be with a
function for squaring numbers.  Our optimizer handles that kind of thing
quite well already, and even before it did, that kind of function is not a
serious bottleneck anyway.  Save the effort of optimization until you know
where it will do the most good.


> Четверг, 11 июля 2013, 0:26 -04:00 от Carl Eastlund <cce at ccs.neu.edu>:
>   Roman,
> Bear in mind that by expanding (square x) to (square x x), that means if
> someone writes
>   (square (perform-expensive-computation))
> then you will expand it to:
>   (* (perform-expensive-computation) (perform-expensive-computation))
> You have to be careful about duplicating inputs in expanded code.
> A lot of the optimizations you are performing manually will be done by the
> Racket optimizer anyway; more if you use Typed Racket with optimizations
> on, and you can always use the Optimization Coach to find more.  A lot of
> the benefits you get from explicit optimization will also come from using
> define-inline from racket/performance-hint.
> If you still prefer to manually expand these things, then I suggest
> something like the following:
> (require (for-syntax syntax/parse))
> (define-syntax (square stx)
>   (define/syntax-parse body #'(* x x))
>   (syntax-parse stx
>     #:literals [square expt]
>     [(square (square e:expr)) #'(expt e 4)]
>     [(square (expt e:expr n:number))
>      (define/syntax-parse 2n (* 2 (syntax-e #'n)))
>      #'(expt e '2n)]
>     [(square (expt e:expr n:expr)) #'(expt e (* n 2))]
>     [(square e:expr) #'(let ([x e]) body)]
>     [square #'(lambda (x) body)]))
> Note that syntax-parse lets you check for numeric inputs in the pattern,
> and define/syntax-parse lets you bind pattern variables.  There isn't a
> problem with "x" here, because the name is not used as a pattern variable,
> it's a variable name introduced by the macro everywhere it occurs.
> If you're unfamiliar with syntax-parse, I encourage you to learn it, but
> you can always use syntax-case and replace define/syntax-parse with
> define/with-syntax from the racket/syntax module.
> On Thu, Jul 11, 2013 at 12:09 AM, Roman Klochkov <kalimehtar at mail.ru<https://e.mail.ru/sentmsg?mailto=mailto%3akalimehtar@mail.ru>
> > wrote:
> I like to make syntax-optimized functions.
> (define-syntax (square stx)
>   (syntax-case stx (square expt)
>     [(square (square expr)) #'(expt expr 4)]
>     [(square (expt expr n)) (if (number? (syntax-e #'n))
>                                                  #`(expt expr #,(*
> (syntax-e #'n) 2))
>                                                  #'(expt expr (* n 2)))]
>     [(square x) #'(* x x)]
>     [square #'(lambda (x) (* x x))]))
> So I can write  (square (expt x 5)) and have evaluate (expt x 10) and I
> can write (map square '(1 2 3)) and it is also works.
> But I dislike to repeate the body of the function in last two cases.
> I tryed
> (define-syntax (square stx)
>   (let ([body #'(* x x)])
>     (syntax-case stx (square expt)
>       ...
>       ...
>       [(square x) body]
>       [square #'(lambda (x) #`body)]))
> but hygiene prevents me from that:  x: unbound identifier in module in: x
> How can I bind template body with some variable? Or how to rewrite the
> syntax to evade duplication?
> --
> Roman Klochkov
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.racket-lang.org/users/archive/attachments/20130711/77ab9f63/attachment.html>

Posted on the users mailing list.