
I like case
but I don’t like typos, so I wrote a safe-case
which I’m using more and more:

The implementation is quite simple: ;; A little case DSL that accepts only a fixed set of symbols
;; and raises an exception on typos (at compile time or at runtime).
(define-syntax-parse-rule (define-safe-case caser (sym ...))
#:with ooo #'(... ...)
(define-syntax-parse-rule (caser stage:expr [(~and condition ((~or (~datum sym) ...) ooo)) body ooo] ooo)
(case stage
[condition body ooo] ooo
[else (error 'caser "unknown case for ~a: ~a\n valid cases: ~a" 'caser stage '(sym ...))])))
(but improvements welcome)

And of course typos in the case argument are caught at runtime:

nice!
I would bind stage
to a temporary variable, to avoid evaluating it twice.

Oh yeah, good point

Also, ~or
is deprecated. Probably use ~or*
instead.

Is it? The docs don’t say so: https://docs.racket-lang.org/syntax/stxparse-patterns.html#%28form._%28%28lib._syntax%2Fparse..rkt%29._~7eor%29%29

> The context-sensitive interpretation of ~or is a design mistake and a common source of confusion. Use ~alt and ~or* instead.

Ah, if you have to read the whole 5-line text and not merely look for “deprecated” then sure :smile:

thanks for the tip

Revision: (define-syntax-parse-rule (define-safe-case caser (sym ...))
#:with ooo #'(... ...)
(define-syntax-parse-rule (caser stage:expr [(~and condition ((~alt (~datum sym) ...) ooo)) body ooo] ooo)
(let ([stage* stage])
(case stage*
[condition body ooo] ooo
[else (error 'caser "unknown case for ~a: ~a\n valid cases: ~a" 'caser stage* '(sym ...))]))))

I guess I need to make it a package


I’m mildly surprised rebellion’s enum (and perhaps other?) types don’t come with a nice way to match on them that is exhaustively checked

Maybe it does, I haven’t checked—did you?

Yes, I just looked. didn’t spot anything. it should be possible, since the type information is present at compile-time for at least enum types

Indeed. Well, safe-case
actually also provides define-safe-case+symbols
which is basically returning an enum as the list of allowed symbols (as well as defining the case
variant of course)

I’d be hesitant to return a more structured type, as lists of symbols are quite convenient

Fair enough! Except lists of symbols don’t prevent me from typo-ing them outside of safe-case
(say, when filling out a data structure that contains a list of symbols) unless the contract requires (or/c 'a 'b 'c)
.

Absolutely. Although a runtime check with memq
is easy to use. The one feature of safe-case
is the compile-time check. (and, arguably, the automatic else
clause that raises an error)

Right, i like the compile-time check, and sometimes I want more :slightly_smiling_face: though probably at that point I want ML’s ADTs or typed racket?
