2
votes

I am reading the Realm of Racket book, and page 175, I see in effect this code:

(struct dice-world (src board gt))
(struct game (board player moves))

(define (no-more-moves-in-world? w)
  (define tree (dice-world-gt w))
  (define board (dice-world-board w))
  (define player (game-player tree))
  player)

The function from the book doesn't return the player, but everything until that line is as in the book. To me, this screams of the need of pattern matching!

And indeed, this works fine and is considerably more readable and explicit to me:

(define (no-more-moves+? w)
  (match w
    [(dice-world _ board (game _ player _)) player]))

However here we still name the variable w for no reason at all. I'd like to be able to pattern match directly on the function parameters, like this (invalid syntax):

(define (no-more-moves2? (dice-world _ board (game _ player _)))
  player)

From my googling so far, this appears impossible? That sounds unbelievable to me? It's probably possible through some macro trickery I guess, but I'm really surprised that it would not be the standard way to write that code from the book? Being a beginner's book, I thought maybe pattern matching will be introduced later, but I don't find it in the index at all?

Also, in case the answer is that it's not possible/not idiomatic, I wonder whether it's the same with other lisps too?

3

3 Answers

5
votes

The form you want is available from the Racket match-plus package, under the name define/match*. Disclaimer: it’s my package. The following code should work fine using that module:

(require match-plus)

(define/match* (no-more-moves2? (dice-world _ board (game _ player _)))
  player)

To install the package, use DrRacket’s Package Manager GUI, or run the following command:

raco pkg install match-plus

I think the rationale for this not being included in Racket itself is as John Clements mentions: using this form, if the pattern match fails, the function simply throws an exception. Still, this is exceedingly useful for your use cases, especially if you put a contract on the function. As noted by the docs:

...only one match clause may be specified since the match patterns are inline with the formals definition. This means an error will be raised if pattern-matching fails, but it is ideal if a function is already contracted in such a way that successful pattern-matching is guaranteed.

(Emphasis mine.)

4
votes

Yes, you could definitely write this macro. I think it would be about the length of your example, maybe shorter.

So, why isn't this idiomatic? I think that the basic answer is this: what should happen if it doesn't match? What if you want to provide multiple patterns? I'm guessing that you might possibly be coming from a Haskell background, where all of the pattern-match-definitions are coalesced into a single function. I think that Scheme is still very wary about breaking the "a whole function definition is wrapped in a single pair of parens," and more broadly, "scopes are delimited by parentheses." It's true that this is getting a bit looser in racket--every lambda can now have definitions immediately inside it--but I think that in general the "taste" would be to use your second form, with a match inside a lambda with a named argument.

But hey--that's what macros are for: use the form you find most readable! I'd be happy to throw that macro together for you if you like.

4
votes

For completeness, there is also define/match, available in #lang racket, that is slightly more verbose than Alexis's solution, and slightly less verbose than a match in the body.

#lang racket
(struct dice-world (src board gt))
(struct game (board player moves))

(define/match (no-more-moves-in-world? w)
  [((dice-world _ _ (game _ p _))) p])