laurent.orseau
2022-7-18 08:16:50

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


laurent.orseau
2022-7-18 08:17:07

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)


laurent.orseau
2022-7-18 08:19:04

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


sorawee
2022-7-18 08:24:35

nice!

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


laurent.orseau
2022-7-18 08:25:17

Oh yeah, good point


sorawee
2022-7-18 08:25:28

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



sorawee
2022-7-18 08:26:34

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


laurent.orseau
2022-7-18 08:27:39

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


laurent.orseau
2022-7-18 08:27:51

thanks for the tip


laurent.orseau
2022-7-18 08:31:41

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 ...))]))))


laurent.orseau
2022-7-18 08:36:50

I guess I need to make it a package


laurent.orseau
2022-7-18 09:48:40

ben.knoble
2022-7-18 13:49:35

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


laurent.orseau
2022-7-18 13:51:46

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


ben.knoble
2022-7-18 13:52:21

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


laurent.orseau
2022-7-18 13:54:43

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)


laurent.orseau
2022-7-18 13:55:57

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


ben.knoble
2022-7-18 13:57:54

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).


laurent.orseau
2022-7-18 14:00:12

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)


ben.knoble
2022-7-18 14:27:26

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?


laurent.orseau
2022-7-18 19:37:44