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))))
(Using a cons
pair works, but I’d like to understand what’s special about values
)
ret
is a function (effectively), and the argument of a function must evaluate to a single value.
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))))
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.
(since superficially it looks similar to features in other languages which have inline pattern bindings)
@yuyang990421 has joined the channel
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.
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.
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.
I personally am in favor of updating docs to recommend this. Others have varying opinions.
While it’s true that syntax-parse
is better in almost every way, keep in mind that:
syntax-case
is inracket/base
, butsyntax-parse
is insyntax/parse
. For Racket Guide / Racket Reference, I think it makes sense to talk about forms thatracket/base
provides rather than talking about forms from extra collections.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.
I don’t think either of those are good reasons to avoid it
the benefits are too huge to not recommend it as the default approach
@jjsimpso has joined the channel
@cris2000.espinoza677 has joined the channel
@ruyvalle has joined the channel
#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…
When you use #:with
, you shouldn’t use supply syntax class
E.g., #:with str #'(A x)
is enough
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
I don’t think it needs to be a splicing syntax class
Actually, let me take that back. I think this could potentially be a bug?
I think it’s both problems
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?
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
Ohhhhh
Indeed
I’ve been bitten by this before where a failing #:with
match in a syntax class caused backtracking without warning
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
I wonder if these error messages can be improved somehow
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.
In this case, I think the correct syntax class is expr
(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 ...))))
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
and the attributes aren’t being used
Yeah, agreed
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 (?* "" "" ""))
@cris2000.espinoza677 Thank you for the puzzle :)
@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.
thanks for the feedback :simple_smile: I definitely agree on the overview/roadmap docs for macros, that’s a great idea.