yilin.wei10
2020-8-21 14:50:56

I’m having trouble understanding why the following code doesn’t work; the for/fold form expects a single return value as expected, but shouldn’t the continuation prompt mean that the iteration isn’t triggered? (let/ec ret (for/fold ([value 0]) ([each (in-range 0 3)]) (if (< value 2) (ret (values 1 2)) (+ value each))))


yilin.wei10
2020-8-21 14:52:35

(Using a cons pair works, but I’d like to understand what’s special about values)


samth
2020-8-21 14:59:07

ret is a function (effectively), and the argument of a function must evaluate to a single value.


sorawee
2020-8-21 15:20:06

What @samth said, but note that the ret function can consume multiple arguments. What you want is simply:

(let/ec ret (for/fold ([value 0]) ([each (in-range 0 3)]) (if (< value 2) (ret 1 2) (+ value each))))


yilin.wei10
2020-8-21 20:18:37

Thank you! This page https://docs.racket-lang.org/reference/eval-model.html?q=values#%28tech._continuation%29 makes more sense - I had been viewing it as a datatype rather than a proper control flow construct.


yilin.wei10
2020-8-21 20:19:44

(since superficially it looks similar to features in other languages which have inline pattern bindings)


yuyang990421
2020-8-22 01:49:10

@yuyang990421 has joined the channel


yuyang990421
2020-8-22 02:15:20

There’s a bug in the following code: #lang typed/racket (define-type Rational (U Integer (Pair Rational Non-Zero-Rational))) (define-type Zero-Rational (U Zero (Pair Zero-Rational Non-Zero-Rational))) (define-type Non-Zero-Rational (Refine [num : Rational] (! num Zero-Rational))) (: make-rational (case-> [-> Rational Non-Zero-Rational Rational] [-> Rational Rational])) (define make-rational (case-lambda [(r1 r2) (cons r1 r2)] [(r) r])) (: numer [-> Rational Rational]) (define (numer x) (cond [(pair? x) (car x)] [else x])) (: denom [-> Rational Non-Zero-Rational]) (define (denom x) (cond [(pair? x) (cdr x)] [else 1])) (displayln denom) (define x (make-rational (make-rational 2 3) 7)) (numer x) (denom x) I am trying to get the Denominator of the Rational number, but it seemed that it doesn’t work, and the problem might be here:(cond [(pair? x) (cdr x)]           [else 1]) the code was just running and no bug had been reported.


michael.hamel80
2020-8-22 03:03:44

When creating macros, is syntax-parse preferred to syntax-case? And if so, should the documentation reflect this? 

I’ve seen a number of people claim that using _syntax-parse_ is strongly preferred over the older _syntax-case_ approach. It’s a much more robust, strongly-typed approach, with a richer pattern language. But in both the <https://docs.racket-lang.org/guide/macros.html?q=syntax-parse|Racket Guide> and the <https://docs.racket-lang.org/reference/Macros.html?q=syntax-parse|Racket Reference>, the Macro chapters focus exclusively on _syntax-case_ – I’m not sure if the existence of _syntax-parse_ is even mentioned. I didn’t even realize that _syntax-parse_ existed until I stumbled across blog postings and other content outside the official Racket site. 

I ended up finding this <https://docs.racket-lang.org/syntax/index.html?q=syntax-parse|set of documentation>, which seems to be the main hub for information about _syntax-parse_ and related functionality. 

Should I focus on learning syntax-parse instead? Or learn the two approaches to macros in parallel? After all, even if it’s true that using _syntax-parse_ is better, there’s a huge amount of existing code out there using _syntax-case_, so I’ll want to understand syntax-case as well.


notjack
2020-8-22 03:50:23

Yes, I would recommend using syntax-parse instead of syntax-case. There’s no benefit to using the latter, unless you’re implementing syntax-parse and therefore can’t use it.


notjack
2020-8-22 03:50:55

I personally am in favor of updating docs to recommend this. Others have varying opinions.


sorawee
2020-8-22 04:03:34

While it’s true that syntax-parse is better in almost every way, keep in mind that:

  1. syntax-case is in racket/base, but syntax-parse is in syntax/parse. For Racket Guide / Racket Reference, I think it makes sense to talk about forms that racket/base provides rather than talking about forms from extra collections.

  2. syntax-parse is slower, though this really only matters if you are doing low-level stuff and your program has heavy computation at compile-time.


notjack
2020-8-22 04:05:57

I don’t think either of those are good reasons to avoid it


notjack
2020-8-22 04:07:05

the benefits are too huge to not recommend it as the default approach


jjsimpso
2020-8-22 04:11:08

@jjsimpso has joined the channel


cris2000.espinoza677
2020-8-22 04:32:52

@cris2000.espinoza677 has joined the channel


ruyvalle
2020-8-22 04:35:46

@ruyvalle has joined the channel


cris2000.espinoza677
2020-8-22 04:36:27

#lang racket (require (for-syntax racket/syntax syntax/parse)) ;(define ? (thunk* '?)) ;(define ?* (thunk* '?*)) (define A (lambda x x)) (begin-for-syntax (define-splicing-syntax-class to-join ;#:datum-literals (? ?*) #:attributes (str) (pattern (~seq str:string)) (pattern ((~datum ?) x:expr) #:with str:string #'(A x)) (pattern ((~datum ?*) x:expr ...+) #:with str:string #'(A x ...))) (define-splicing-syntax-class stmt-str (pattern str:string) (pattern (x:to-join ...+) #:with str:string #'(string-append x.str ...)))) (define-syntax (t stx) (syntax-parse stx [(_ t:stmt-str) #'t.str])) (define-syntax (v stx) (syntax-parse stx [(_ t:to-join) #'t.str])) (v "") (v (? "")) (v (?* "" "" "")) I dont understand why the last two don’t work…


sorawee
2020-8-22 04:47:46

When you use #:with, you shouldn’t use supply syntax class


sorawee
2020-8-22 04:47:59

E.g., #:with str #'(A x) is enough


notjack
2020-8-22 04:49:31

sorawee is correct, though I think it’s odd that to-join is a splicing syntax class but it doesn’t use ~seq in every pattern


notjack
2020-8-22 04:50:20

I don’t think it needs to be a splicing syntax class


sorawee
2020-8-22 04:51:13

Actually, let me take that back. I think this could potentially be a bug?


notjack
2020-8-22 04:52:19

I think it’s both problems


sorawee
2020-8-22 04:52:19

I mean, what I wrote above is definitely true: using #:with str #'(A x) works. But probably #:with str:string #'(A x) should work too?


notjack
2020-8-22 04:52:50

it shouldn’t, #'(A x) isn’t a literal string so #:with str:string won’t match it, which will cause the whole to-join pattern to backtrack


sorawee
2020-8-22 04:53:02

Ohhhhh


sorawee
2020-8-22 04:53:13

Indeed


notjack
2020-8-22 04:53:16

I’ve been bitten by this before where a failing #:with match in a syntax class caused backtracking without warning


notjack
2020-8-22 04:53:57

the error message doesn’t indicate that the #:with clause is where matching failed, so you end up looking at the actual pattern in the pattern clause and wondering why tf it’s not matching


notjack
2020-8-22 04:54:31

I wonder if these error messages can be improved somehow


sorawee
2020-8-22 04:55:54

OK, so let me summarize: using syntax class in #:with works (I thought it doesn’t, but that’s not true). However, you must specify the correct syntax class, or not specify at all if there’s no constraint.


sorawee
2020-8-22 04:56:09

In this case, I think the correct syntax class is expr


sorawee
2020-8-22 04:56:35

(begin-for-syntax (define-splicing-syntax-class to-join ;#:datum-literals (? ?*) (pattern (~seq str:string)) (pattern ((~datum ?) x:expr) #:with str:expr #'(A x)) (pattern ((~datum ?*) x:expr ...+) #:with str:expr #'(A x ...))))


notjack
2020-8-22 04:57:10

I think not using any syntax class in these cases would be best, since it’s a syntax object that’s being constructed right there and we know it will always match


notjack
2020-8-22 04:57:15

and the attributes aren’t being used


sorawee
2020-8-22 04:57:23

Yeah, agreed


notjack
2020-8-22 04:57:39

this is what I came up with:

#lang racket (require syntax/parse/define) (define A (lambda x x)) (begin-for-syntax (define-syntax-class to-join #:datum-literals (? ?*) #:attributes (str) (pattern str:string) (pattern (? x:expr) #:with str #'(A x)) (pattern (?* x:expr ...+) #:with str #'(A x ...)))) (define-simple-macro (v t:to-join) t.str) (v "") (v (? "")) (v (?* "" "" ""))


notjack
2020-8-22 04:59:27

@cris2000.espinoza677 Thank you for the puzzle :)


michael.hamel80
2020-8-22 06:10:21

@notjack @sorawee 

Thanks for the responses re syntax-case vs syntax-parse! I’ll aim to use syntax-parse once I’m at the level of writing serious macros. But I’ll familiarize myself with syntax-case as well, since we see it everywhere, including non-Racket Scheme code. 

Being new I’ll avoid involving myself in any debates as to what should be the recommended default, and whether syntax-parse and related topics belong in the core docs. But I will suggest that it would be beneficial for the core docs to provide more of an overview of the overall Racket macro ecosystem. Even if syntax-parse is not included in those docs, there could at least be a signpost saying, “you might also be interested in learning about syntax-parse, here’s your link”. 

Along the same lines, while Racket documentation is extremely comprehensive and well-structured, for the newcomer it can seem like information overload at times. On the topic of macros specifically, it would be nice to have more of a learning/study roadmap laid out, a clear pathway from beginner-> intermediate- > expert.


notjack
2020-8-22 06:13:06

thanks for the feedback :simple_smile: I definitely agree on the overview/roadmap docs for macros, that’s a great idea.