1

If Racket's match macro were a function I could do this:

(define my-clauses (list '[(list '+ x y) (list '+ y x)]
                         '[_ 42]))

(on-user-input
  (λ (user-input)
    (define expr (get-form-from-user-input user-input)) ; expr could be '(+ 1 2), for example.
    (apply match expr my-clauses)))

I think there are two very different ways to do this. One is to move my-clauses into macro world, and make a macro something like this (doesn't work):

(define my-clauses (list '[(list '+ x y) (list '+ y x)]
                         '[_ 42]))

(define-syntax-rule (match-clauses expr)
  (match expr my-clauses)) ; this is not the way it's done.

; "Macros that work together" discusses this ideas, right? I'll be reading that today.

(on-user-input
  (λ (user-input)
    (define expr (get-form-from-user-input user-input)) ; expr could be '(+ 1 2), for example.
    (match-clauses expr)))

The alternative, which might be better in the end because it would allow me to change my-clauses at runtime, would be to somehow perform the pattern matching at runtime. Is there any way I can use match on runtime values?

In this question Ryan Culpepper says

It's not possible to create a function where the formal parameters and body are given as run-time values (S-expressions) without using eval.

So I guess I'd have to use eval, but the naive way won't work because match is a macro

(eval `(match ,expr ,@my-clauses) (current-namespace))

I got the desired result with the following voodoo from the guide

(define my-clauses '([(list'+ x y) (list '+ y x)]
                     [_ 42]))

(define-namespace-anchor a)
(define ns (namespace-anchor->namespace a))
(eval `(match '(+ 1 2) ,@my-clauses) ns) ; '(+ 2 1)

Is the pattern matching happening at runtime now? Is it a bad idea?

Community
  • 1
  • 1
spelufo
  • 634
  • 3
  • 19
  • At first you describe wanting `match` to be a function -- but the part about changing clauses at runtime part doesn't seem to be your original, essential reason why you want that. Can you say more about your original/main reason you wanted `match` to be a function? (Maybe if we understand what you're trying to do, we could suggest more ideas?) – Greg Hendershott Jan 02 '15 at 15:15

3 Answers3

2

To answer the first part of your question (assuming you don't necessarily need the match clauses to be supplied at runtime):

The key is to:

  1. Define my-clauses for compile time ("for syntax").

  2. Reference that correctly in the macro template.

So:

(begin-for-syntax
  (define my-clauses (list '[(list '+ x y) (list '+ y x)]
                           '[_ 42])))

(define-syntax (match-clauses stx)
  (syntax-case stx ()
    [(_ expr) #`(match expr #,@my-clauses)]))
Greg Hendershott
  • 16,100
  • 6
  • 36
  • 53
1

The pattern matching is happening at runtime in the last example.

One way to check is to look at the expansion:

> (syntax->datum 
   (expand '(eval `(match '(+ 1 2) ,@my-clauses) ns)))
'(#%app eval (#%app list* 'match ''(+ 1 2) my-clauses) ns)

Whether is a good idea...

Using eval is rather slow, so if you call it often it might be better to find another solution. If you haven't seen it already you might want to read "On eval in dynamic languages generally and in Racket specifically." on the Racket blog.

Shannon Severance
  • 18,025
  • 3
  • 46
  • 67
soegaard
  • 30,661
  • 4
  • 57
  • 106
1

Thank you both very much, your answers gave me much food for thought. What I am trying to do is still not very well defined, but I seem to be learning a lot in the process, so that's good.

The original idea was to make an equation editor that is a hybrid between paredit and a computer algebra system. You enter an initial math s-expression, e.g. (+ x (* 2 y) (^ (- y x) 2). After that the program presents you with a list of step transformations that you would normally make by hand: substitute a variable, distribute, factor, etc. Like a CAS, but one step at a time. Performing a transformation would happen when the user presses the corresponding key combination, although one possibility is to just show a bunch of possible results, and let the user choose the new state of the expression amongst them. For UI charterm will do for now.

At first I thought I would have the transformations be clauses in a match expression, but now I think I'll make them functions that take and return s-expressions. The trouble with choosing compile time vs runtime is that I want the user to be able to add more transformations, and choose his own keybindings. That could mean that they write some code which I require, or they require mine, before the application is compiled, so it doesn't force me to use eval. But it may be best if I give the user a REPL so he has programmatic control of the expression and his interactions with it as well.

Anyway, today I got caught up reading about macros, evaluation contexts and phases. I'm liking racket more and more and I'm still to investigate about making languages... I will switch to tinkering mode now and see if I get some basic form of what I'm describing to work before my head explodes with new ideas.

spelufo
  • 634
  • 3
  • 19