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 racket-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to