laurent.orseau
2020-5-12 09:12:25

Racket is really missing a form of cond that reduces rightward shift and parenthesis. I know several people have designed some variants of cond over the years to do that. Here’s a very simple one that fits my needs almost perfectly: (define-syntax (begin/cond stx) (syntax-parse stx [(_ body:expr ... (~seq #:cond [test test-body ...] ...) (~seq #:else else-body ...)) #'(begin body ... (cond [test test-body ...] ... [else (begin/cond else-body ...)]))] [(_ body:expr ...) #'(begin body ...)])) Example with default racket: (displayln "start") (define y 2) (cond [(> y 3) 'y>3] [(not 5) 'well_nope] [else (displayln "do") (define x (+ y 2)) (cond [(> x 3) 'x>3] [else (define z (+ x y)) (cond [(< z 10) 'z<10] [else 'other])])]) Same with begin/cond: (begin/cond (displayln "start") (define y 2) #:cond [(> y 3) 'y>3] [(not 5) 'well_nope] #:else (displayln "do") (define x (+ y 2)) #:cond [(> x 3) 'x>3] #:else (define z (+ x y)) #:cond [(< z 10) => values] #:else 'other) I also find it to be more readable.

What’s your favorite variant of cond? Do you like this one (why/why not)?


rokitna
2020-5-12 10:21:14

For that example, my favorite flavor or cond would be if, thanks to Parendown:


 ; `w-` is `let` with fewer parens
 (require (only-in lathe-comforts w-))

 (begin (displayln "start")
 #/w- y 2
 #/if (> y 3) 'y>3
 #/if (not 5) 'well_nope
 #/begin (displayln "do")
 #/w- x (+ y 2)
 #/if (> x 3) 'x>3
 #/w- z (+ x y)
 #/if (< z 10) 'z<10
   'other)

More broadly, I also tend to find I use pattern matching a lot more than I use if.


scolobb-slack
2020-5-12 10:38:47

While the rightward shift is clearly a problem, I think I prefer the standard cond because it’s syntax is simpler. Also, I think that rightward shift can often be contained by factoring stuff into smaller functions. Now, you’ll tell me that the “simpler syntax” argument is a matter of habit, and you’ll be right :shrug:


scolobb-slack
2020-5-12 10:39:51

And indeed, pattern matching often cuts it perfectly for me, both in expressivity and in reducing the rightward shift


alexknauth
2020-5-12 13:06:53

For a variant of cond I think we could take inspiration from Alexis King’s do notation from data/monad. It allows a similar mixing of “do clauses” with normal definitions and expressions, just like we want here with cond. However being based on Haskell do notation it uses an infix <- symbol in every “do clause”: > (do [x <- (just 7)] (define (calls-b) (add1 (b))) (define (b) (- x)) [y <- (just (calls-b))] (pure (* 2 y))) (just -12) For the cond version, an extra infix symbol could be => or -> or then or :: (cond (displayln "start") (define y 2) [(> y 3) => 'y>3] [(not 5) => 'well_nope] (displayln "do") (define x (+ y 2)) [(> x 3) => 'x>3] (define z (+ x y)) [(< z 10) => => values] [else => 'other]) Or perhaps since Racket isn’t Haskell a prefix-style distinguishing symbol would work better, like if: (cond (displayln "start") (define y 2) [if (> y 3) 'y>3] [if (not 5) 'well_nope] (displayln "do") (define x (+ y 2)) [if (> x 3) 'x>3] (define z (+ x y)) [if (< z 10) => values] [else 'other])


sorawee
2020-5-12 13:11:52

Would be nice to get rid of the last else and just write 'other.


sorawee
2020-5-12 13:15:04

For my personal project, I use this macro: https://github.com/sorawee/my-utils/blob/master/cond.rkt#L7


sorawee
2020-5-12 13:16:18

Usage:

(cond [x 1] [#:let y 2] [(foo? y) 3] [(bar? y) 4] [#:let z 5] [else (+ y z)])


sorawee
2020-5-12 13:16:45

It wouldn’t directly support that (displayln "do")


sorawee
2020-5-12 13:17:12

Though it’s technically possible: [#:let _ (displayln "do")]


alexknauth
2020-5-12 13:18:46

Another possibility is taking inspiration from syntax-parse, which allows #:do [def-or-expr ...] in between pattern-directives. With cond that would look like: (cond #:do [(displayln "start") (define y 2)] [(> y 3) 'y>3] [(not 5) 'well_nope] #:do [(displayln "do") (define x (+ y 2))] [(> x 3) 'x>3] #:do [(define z (+ x y))] [(< z 10) => values] [else 'other])


sorawee
2020-5-12 13:19:09

And… I discovered that my macro also supports #:do lol


sorawee
2020-5-12 13:19:13

I just forgot that it exists.


sorawee
2020-5-12 13:19:57

Though the syntax is slightly different


sorawee
2020-5-12 13:20:33

One advantage of let is that it’s like let*


sorawee
2020-5-12 13:20:48

You can keep shadowing the same id over and over again


sorawee
2020-5-12 13:20:59

That’s not possible with #:do [(define ...)]


laurent.orseau
2020-5-12 13:22:35

Personally I prefer shadowing to have an parenthesized scope


sorawee
2020-5-12 13:27:56

I dunno. I found myself using the “update” idiom a lot. Like:

(define (foo x) (define normalized-x (normalize x)) (define enhanced-x (enhance normalized-x) (define updated-x (update enhanced-x)) (f updated-x))) or equivalently:

(define (foo x) (let* ([x (normalize x)] [x (enhance x)] [x (update x)]) (f x))) It would be really nice to be able to write this instead:

(define (foo x) (define* x (normalize x)) (define* x (enhance x)) (define* x (update x) (f x)))


laurent.orseau
2020-5-12 13:32:59

For this I use (with [it x] (normalize it) (enhance it) (update it) (f it))


sorawee
2020-5-12 13:34:52

(just do it)


alexknauth
2020-5-12 13:49:47

Or (~> x normalize engance update f) using the threading package


laurent.orseau
2020-5-12 13:51:18

yeah, but often the argument is not in the first place and then it becomes less clean


alexknauth
2020-5-12 13:55:54

Then the _ hole marker in threading becomes useful. (~> x (expt 2 _) add1 (range _ 0 -1) (map log _))


laurent.orseau
2020-5-12 15:18:00

That’s what I mean by ‘less clean’. Not hygienic, and there are some parenthesized expressions mixed with procedures. Also the notation is too hacky for my taste.


hoshom
2020-5-12 16:32:56

@hoshom has joined the channel


notjack
2020-5-12 17:20:25

Oh boy, do I have Opinions here


notjack
2020-5-12 17:22:53

Just to get this out of the way first: the hole marker should be required when threading, always.


deactivateduser60718
2020-5-12 22:11:50

notjack
2020-5-12 22:26:26

This is great!


notjack
2020-5-12 22:27:03

Drive by comment: the parser-entry-input/c contract is a little confusing to me. Can it be simplified? For instance, can it be just (sequence/c char?)?


deactivateduser60718
2020-5-12 23:45:30

I see what you mean. Given the spec’s requirements it might need to be (sequence/c (or/c token? char?)).


deactivateduser60718
2020-5-13 03:30:35

I’m having second thoughts about the package and collection names. css3 as a collection name is a misnomer since the numerical levels don’t refer to CSS as a whole, but as the “level” of a specification module defined by the W3C. Since the css package name is already taken, I have to think more about what else to call it. css-syntax3 might be appropriate for a collection name, though.