I've been following the list since I started to play with Racket (~6
months), but I've never had the courage to write a macro.  It was like
a kind of magic that I wanted to manipulate, but I was afraid to do
that.


## Context. Story time!

I discovered Racket because a friend asked me to teach him to how to
program.  Inspired by the Lego kit for robotic that I saw years ago, I
created a little language (move forward and rotate) and started to ask
him to accomplish some tasks with the language.  The day after that, I
searched for "teach kids how to program" (my friend is not a kid
.. but what would be a better way to teach someone?) and I found
"Turtles" which led me to read an article from Papert.  The text was
mind blowing.  I am Brazilian, and I can't say for all of us, but I
was raised with a black/white (or right/wrong) mindset.  This mindset
shaped the way that I've been approaching life.  Basically, I always
had one chance to get things right.  Yeah!  I love programming and it
is a nightmare to try to get things right at first (it is so easy to
be stuck with this mindset).  So, even though I loved programming, I
also hated it.  But the ideas in Papert's article was liberating, I've
never thought that exploring without fear of failure would be so
natural.  I wanted more of it!  I kept searching and found "The Little
Lisper", which I consumed it vigor.  Then I found some of Matthias'
work in programming education and finally Racket.  As I said, I've
been following the list for a while. And man, that is a wonderful
community.  OK! My friend and I never really started those classes,
but I am grateful for his first interest on that.

I've learned that if I want to learn something I have to explore it
without fear!  Then, I decided to do the [Advents of Code
2018](https://adventofcode.com/2018) in
Racket.  My goal was use functional programming and immutability as
much as I can.  I started with Typed Racket, but I found hard to
freely explore while having to satisfy the compiler.  `#lang racket` was
the best option for me.

What about writing macros?  I've been postponing it until April 26th.
I was watching [Inside Racket Seminar 6. Sam Tobin-Hochstadt on
match](https://www.youtube.com/watch?v=IikGK8XP5_Q)
and thinking how these people can write such big programs in an
untyped laguange.  I remembered to see something about "bottom-up
programming" in [Racket
pages](https://docs.racket-lang.org/style/Units_of_Code.html) and I
started to search for it.  I found
the key idea: building the solution from bottom-up give you chance to
build a language for the problem.  It became clear when I saw [Daniel
Friedman & Jason Hemann - Implementing a
microKanren](https://www.youtube.com/watch?v=0FwIwewHC3o) in action
(by the
way, I am a big fan of Friedman) and [Bottom Up vs Top Down Design in
Clojure - Mark Bastian](https://www.youtube.com/watch?v=Tb823aqgX_0).

I went back to my problem and found that I have a code like

    (define empty-unit #\.)
    (define (empty-unit? v) (eq? #\. v))
    (define rock-unit #\.)
    (define (rock-unit? v) (eq? #\# v))

which I really wanted to express like

    (define-enum unit (empty #\.) (rock #\#))

It is time for writing my first macro!


## Macro time!

My first try was something like this

    (define-syntax (define-unit.wrong stx)
      (syntax-case stx ()
        [(_ name value)
         (let ([name? (string->symbol (format "~a?" name))])
           #'(begin
       (define name value)
       (define (name? other-value) (eq? value other-value))))]))

which failed with

    ; /Users/wander/myprojects/aoc/2018/day17.rkt:118:48: name:
pattern variable cannot be used outside of a template
    ;   in: name
    ; [Due to errors, REPL is just module language, requires, and stub
definitions]

I didn't understand the error message, and had no idea of how to move
forward.  I searched the error message and found [Greg Hendershott's
Fear of Macros - Pattern
matching](https://www.greghendershott.com/fear-of-macros/pattern-matching.html)
(by the way, I am big fan of Greg).
After reading his write-up, became easier to read the Racket
documentation.

That was my first macro:

    (require (for-syntax racket/syntax))
    (define-syntax (define-unit stx)
      (syntax-case stx ()
        [(_ name value)
         (with-syntax ([name? (format-id #'name "~a?" #'name)])
           #'(begin
       (define name value)
       (define (name? other-value) (eq? value other-value))))]))

    (define-unit empty-unit #\.)
    (define-unit rock-unit #\#)
    (define-unit dry-unit #\d)
    (define-unit water-unit #\~)

Dude!  I was so excited!  I did my first macro and it wasn't that
scare.  I barely could sleep because I did something cool and I also
wanted to improve my solution with a "more complex syntax". The next
night, I did my second macro:

    (define-syntax (define-enum stx)
      (syntax-case stx ()
        [(_ name (e v) ...)
         #`(begin
    #,@(for/list ([x (syntax->list #'((e v) ...))])
          (define xs (syntax->list x))
          (define e-name (car xs))
          (define e-value (cadr xs))
          (with-syntax* ([elem (format-id e-name "~a-~a" #'name e-name)]
         [elem? (format-id #'elem "~a?" #'elem)]
         [value (cadr xs)])
    #'(begin
        (define elem value)
        (define (elem? other-value)
          (eq? elem other-value))))))]))

    (define-enum unit
      (dry #\d)
      (empty #\.)
      (rock #\#)
      (water #\~))

    (unit-dry? unit-empty) ; returns #f


## Thanks you all

My journey has much more than I shared, and there are many others that
inspired me up to this point.  Thank you all!  In short, I am felling
right now on shoulder of giants!

-- 
You received this message because you are subscribed to the Google Groups 
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to