
Fantastic!

someone mentioned : (define v (make-vector 2 (vector)))
(eq? (vector-ref v 0)
(vector-ref v 1))
; #t
eq? tests if they point to the same object. Maybe we can stick this in. Either way lol, wasted a few hours making sure i wasnt losing my mind

I don’t think this is “tricky behavior”, and I don’t think providing mutable data as the second argument to make-vector
is a good idea in almost any instance.

make-vector
doesn’t do anything different for (make-vector 2 (vector))
vs (make-vector 2 17)

yeah, not sure where you saw that code, but I’ve done that several times myself. Sadly, there’s actually multiple issues, though.
- The argument is only evaluated once (the obvious problem), so it’s the same vector.
- Even if you used
(build-vector 2 (lambda (i) (vector))
the same problem will exist, because(vector)
is an empty vector, which is likely a special case in the implementation (it may be#t
for some implementations and#f
for others).

(define x (build-vector 2 (lambda (i) (vector))))
(eq? (vector-ref x 0) (vector-ref x 1))
; ==> #t

(that’s on 8.1 [cs])

Yes, that’s true, but there’s a bigger problem than eq?
, which is that the empty vector is totally useless

disagree. You may not be able to add to it, but a vector of vectors means you can replace it. Being able to call vector-length
on it or for
to do something meaningful on an “empty” or “non-empty” entry is useful.

Otherwise you’re using #f
and then special casing/running checks in lots of other functions.

Thanks all for your help!

Have another question. What is the difference between make-hash, make-hasheq, and make-hasheqv
?

@kartiksabharwal7 has joined the channel

Scheme was my first language. I started with The Little Schemer and SICP. But now I’m using Common Lisp. What are some good reasons to switch to Racket as my main Lisp instead?

Note: I personally prefer Common Lisp, mainly because I like the “fail cases” more (e.g. (car nil) ;=> nil
as opposed to an exception) and defmacro
over define-syntax
.
But, I use Racket, too, and what I like (in order):
• Batteries included (HTTP, JSON, etc.) • A pretty good, simple editor. It’s not emacs, which sucks. But, it’s not emacs, which is a plus. :slightly_smiling_face: • A debugger (note: I like LispWorks debugger more, but LW costs a sizeable chunk of $$). • The FFI does a lot of nice things. • Package management (raco), etc.

Basically, if you’re comparing LW or Franz to Racket, you know CL, and $$ isn’t an obstacle, then use LW or Franz. Otherwise, Racket is a great, free alternative (> SBCL IMO) that’s well maintained and you can do a LOT with out of the box.

Mhmm ok thanks

Hi everyone. I have a theoretical doubt. If I have an undefined and unbound identifier. Can I link it to a macro according to a pattern in the name? For example: (define-syntax-rule (http:// URL ...)
(list 'http:// '(URL ...)))
'<http://google.com> ; => ID
(<http://google.com>)
; instead of
(http:// <http://google.com\|google.com>)

I know I can create a macro that reads and processes the id, but I just want to know if it is possible to detect a call to an id that does not exist and see what can be done.

You might want to look at #%top
if I understand what you’re asking

I have a Racket program, compiled to a Linux executable with raco exe -o myprogram myprogram.rkt
. I can run ./myprogram --help
as expected. However, if I post-process the executable with strip myprogram
, it gets smaller (from 53977863 to 44619536 bytes), but when I run the stripped version ./myprogram --help
, I get open-input-bytes: contract violation
expected: bytes?
given: #<eof>
Is this a known problem? (I can’t find anything in the ticket system, but I guess this is difficult to search for.)

Racket’s macro system is the state of the art. It makes building new languages easy, and it turns out that tiny languages are often a great way to frame a solution to a problem domain.


Does objdump -h
before strip
show a .rackprog
section? If so, is the section still there after strip
?

I made a mistake. I had generated the program with raco exe --orig-exe
. If I leave out --orig-exe
, I get the normal help output (but I want a standalone binary).

@mflatt No, there’s no .rackprog
section (even before strip
). There’s no difference in the output of objdump
before and after the strip
run.

It looks like --orig-exe
disables ELF mode for raco exe
, but it shouldn’t…

@samth Regarding ticket 3726, I’m actually on Fedora, but I installed Racket 8.1 from the download section of the Racket website. The downloaded installer file is racket-8.1-x86_64-linux-cs.sh
.

I meant that there’s a discussion of stripping in that ticket, but you confirmed that isn’t it

As I understand the ticket, this is about something different (but I may be wrong and the ticket may be relevant).

@mflatt Should I enter a bug report?

It looks like there’s some mismatch between --orig-exe
and ELF mode.

You can submit a bug report if you want to make sure I don’t forget to look into it more.

Meanwhile, you can avoid the problem by not using strip
, since the starting executable is already stripped.


@mflatt I was just curious if I could reduce the size. I didn’t know the binary was effectively stripped already.

If I can’t reduce the size with strip
anyway, the ticket has rather low priority for me (unless the problem can cause more significant problems in other contexts).

Just out of curiosity: Is there a way to get smaller binaries while using --orig-exe
?

The executables can be about 30MB smaller if you configure and build with --enable-compressboot
. See also the middle part of https://groups.google.com/g/racket-users/c/L1gP4JwhS7A/m/EWT5YIe1AQAJ

(I did change the default for Windows)

I guess I’d usually prefer faster startup times over smaller executables. :slightly_smiling_face:

But of course I realize it depends on the circumstances whether a smaller size or shorter startup time is preferable.

I’m not trying to be argumentative here, but I’m honestly curious what about Racket’s macro system makes it “the” state of the art in your opinion. Ignoring hygenicity (is that even a word :slightly_smiling_face:?), I’ve always found CL macros to be so much easier to reason and implement over Scheme’s. There’s literally just defmacro
and macrolet
if you want a local macro.
I have yet to successfully make any reasonably complex macro in Scheme/Racket with its barrage of define-syntax
, syntax-rules
, syntax-case
, syntax-parse
, syntax, quasisyntax, #
, #&
, #'
, etc. I’m sure they all have incredible value, and I’m not trying to criticize them. But, I can build very complex lexer/parser macros in CL quite easily with nothing more than backquote and gensym.
The above said. I do believe Racket’s #lang
construct, allowing for custom readers and expanders does lend itself to being great at building languages and not DSLs / “a language embedded in what would otherwise be Racket/CL code”. I just wish it was trivial to actually do. The best examples I’ve had anyone point to in this Slack or online is Beautiful Racket, which basically states (my paraphrasing) “use #lang br
because I attempt to wrap a lot of what’s difficult to use in Racket to make a language.” Which isn’t exactly a rousing endorsement. :wink:

I just tried again and I’m not even getting a confirmation page after entering my package data. I guess I’d be supposed to get some form of confirmation? Is the lack of a confirmation page a sign that something more fundamental is wrong that won’t be fixed by an update to Racket 8.1?
What do you suggest how long I should wait until I try again?

Regarding all the macro-building forms (syntax-rules
, syntax-case
, syntax-parse
; and the templates syntax
/#'
and quasisyntax
/#
) they are all just macros built using
define-syntax,
syntax-quote, and
syntax-local-value. There’s also an implementation of
defmacro, if you want to use it. Also,
syntax-rulesand
syntax-caseare just older, simpler forms for building macros;
syntax-parseis the most modern and powerful.
Racket’s set-of-scopes model is the most robust way to manipulate syntax with binding forms, especially in the presence of macros. One particularly powerful use of the macro system is to build new languages that have their own macro system. You can also use the macro system to implement at type system at the same time: for example, you can implement a feature-rich, ML-like language with a type-aware macro system in less than 500 lines of code. (Using the
turnstile` language, you can get that down to 200 lines.) Implementing correct type checking and inference with such little work is an incredible feat.
Racket’s module system is an invaluable component of the language. Given that macros can perform arbitrary computations, it’s critical that there is a well-defined notion of staging to ensure that code and its side-effects are executed at the correct time, and to prevent bug-inducing interference between macros and the code they generate.

> But, I can build very complex lexer/parser macros in CL quite easily with nothing more than backquote and gensym. and car
cadr
caadr
cddar
,@
,
etc. You have to at least admit our pattern matching is very convenient. syntax-parse
(which I use for everything, forget syntax-rules/case) also lets you put all the error checking in the macro pattern. It takes minimal effort in racket to make your error messages point back to the original syntax rather than some random place in the expanded code.
another advantage is with redefining core forms. You can redefine lambda
, if
, set!
and not worry about ruining everyone’s code. You can also redefine function application. here’s an example that redefines set!
http://barzilay.org/Swindle/ turnstile redefines function application to perform typechecking. more examples…

Is it possible to create a lambda that requires keywords and gives error messages when they are missing that point to the call site? I’m using make-keyword-procedure
and I’ve tried adding procedure-reduce-keyword-arity
— but every scenario leads to errors pointing at the function creation site.

What I need is a much better intro to scheme macros than the last one I did probably 10+ years ago. :slightly_smiling_face:
And I agree that racket’s match
is great. I still like CL more than scheme overall. But, sadly, CL is a dead-end when it comes to “batteries included”, and I’m not a fan of QuickLisp. I love LispWorks, but just wish it wasn’t so expensive for a hobbyist. Especially once you factor in 64-bit and that you’re paying per-platform.

Can you give an example? I’m seeing errors pointing to the call site, so I’m probably missing something important about your case.
[Edit: yeah, I just missed the significance of your using make-keyword-procedure
.]

A little example: this is a hygienic let
macro that expands into an immediate function application. It checks that the right syntax is used and it requires that variable names are unique. Yes, I could implement it with s-exprs and gensym
, but it would be unnecessarily complicated. #lang racket/base
(require (for-syntax racket/base)
syntax/parse/define)
(define-syntax-parser let-over-lambda
[(_ ([var:id val:expr] ...) body:expr)
#:fail-when (check-duplicate-identifier (syntax->list #'(var ...)))
"duplicate variable name in let-form"
#'((λ (var ...) body) val ...)])

If you want a good introduction to Racket macros… • Greg Hendershott’s Fear of Macros (http://greghendershott.com/fear-of-macros/) starts from the very fundamentals with define-syntax
and builds up to syntax-parse
. • The documentation for the syntax/parse
library includes good examples of how to use all the powerful features, starting with simple macros (https://docs.racket-lang.org/syntax/stxparse-intro.html?q=syntax-parse).

It happens with the example from the docs, e.g.:

#lang racket
(define orig-show
(make-keyword-procedure (lambda (kws kw-args . rest)
(list kws kw-args rest))))
(define show (procedure-reduce-keyword-arity
orig-show 3 '(#:init) '(#:extra #:init)))
(orig-show #:test 12 1)
(show #:extra 1 2 3 4)

The error points to line 4, the lambda

(procedure-reduce-keyword-arity
is saying that #:init
is required)

In DrRacket 7.9, (show #:extra 1 2 3 4)
is highlighted as the source of the error.

Same in 8.0.

so I have this macro: (define (keyword->symbol s)
(string->symbol (keyword->string s)))
(define-syntax (define-renderer stx)
(syntax-parse stx
[(_ (FN-NAME . ARGS)
(KEY VALUE ...)
FN-BODY ...)
#'(define (FN-NAME (~@ . ARGS))
(hash 'function (λ ()
FN-BODY ...)
(~@ (keyword->symbol (quote KEY)) VALUE ...)))]
[(_ (FN-NAME . ARGS)
()
FN-BODY ...)
#'(define (FN-NAME (~@ . ARGS))
(hash 'function (λ () FN-BODY ...)))]
[_ (raise-syntax-error 'define-renderer "bad syntax")]))
which basically packs a function definition into a hash table. So, like, (define-renderer (fn-name arg1) (#:key value)
function-body)
; =>
(define (fn-name arg1)
(hash 'function (lambda () function-body)
'key value))
The problem is, if I were to accidentally omit the (#:key value)
or ()
, the first element of the function body gets read as it instead. Is there a way out of this?

so like, (define-renderer (fn-name arg1)
(define x 3)
x)
has (define x 3)
read as KEY VALUE ...

preferably there’d be a nicer syntax error

that’s the X — the Y problem, in my mind, is excluding non-keyword keys in the pattern. don’t know how to do that.

(or coming up with a better syntactic form.)

With syntax-parse, you can specify what kind of syntax something should be. If you change KEY
to KEY:keyword
, it will only match keywords, and throw a syntax error otherwise.

(more standard syntax classes like keyword
can be found here: https://docs.racket-lang.org/syntax/Library_Syntax_Classes_and_Literal_Sets.html?q=keyword)

thank you very much!

just a minute, and I can show how I’d write it…

This should work the same, but it makes sure that the right kind of syntax is used. (define-syntax (define-renderer stx)
(syntax-parse stx
[(_ (FN-NAME:id ARGS:id ...)
({~seq KEY:keyword VALUE:expr} ...)
FN-BODY:expr ...)
#'(define (FN-NAME (ARGS ...))
(hash 'function (λ ()
FN-BODY ...)
{~@ (keyword->symbol 'KEY) VALUE} ...))]))

Well, the example you posted has only one keyword and zero-or-more values. The version I just posted takes zero-or-more keyword-value pairs. I’m not sure which behavior you wanted.

I thought @hazel needed one-or-more key-value pairs, but that would be an easy change from your code,

no it’s zero or more

much appreciated

my macro abilities are limited

Macros are almost their own branch of programming, and I’ve probably neglected regular programming to focus on macros :sweat_smile:

I might be wrong, but it looks like the second pattern clause is taking care of the zero key value pairs, no?

I think that was the intent, but the same behavior can be handled with one clause.

@hazel I’m sorry, I think I neglected to answer your original question… Did you want the option of omitting the empty ()
in the case that there are no key-value pairs?

@jaz is that with debugging on in DrRacket?

I tried in 7.9 [cs] and I get the error location too.

It’s 7.9 [bc] that has the issue… is that crazy?

no, that’s not crazy — the error locations are based on walking the C stack when the error happens, so it can vary based on implementation

huh… ok that’s a reason to switch then

@samth Yeah, that’s with debugging on

Without debugging it behaves as @dan.ml.901 describes

AFK for a bit but I am interested in the variables here to get better errors.

to get good errors (at cost of some performance) you can use errortrace (you probably already know about that)

should i report this ?

a terminal also pops up that i cant close.. or one that i do close will crash racket

yes

ill try to see if i can isolate it to a reproducable case… this script is a bit large and the internal error popup happens very rarely..

I did not no

I just wanted it to be syntactically invalid to omit it

instead of failing in an unexpected way

Here’s an example:
#lang racket
(require syntax/parse/define
(for-syntax racket/string)
(only-in racket [#%top racket:#%top]))
(define-syntax-parser #%top
[(_ . x:id)
#:do [(define s (symbol->string (syntax-e #'x)))]
#:when (string-prefix? s "http://")
#:do [(define s* (string->symbol (substring s (string-length "http://"))))]
#`(list (quote http://) (quote #,s*))]
[(_ . x) #'(racket:#%top . x)])
<http://www.google.com> ; '(http:// <http://www.google.com\|www.google.com>)

Oops, the macro I posted had a couple errors. This version fixes them: (define-syntax (define-renderer stx)
(syntax-parse stx
[(_ (FN-NAME:id ARGS:id ...)
({~seq KEY:keyword VALUE:expr} ...)
FN-BODY:expr ...+)
#'(define (FN-NAME ARGS ...)
(hash 'function (λ ()
FN-BODY ...)
{~@ (keyword->symbol 'KEY) VALUE} ...))]))

This version also describes what part was missing, if you accidentally omit part of the syntax, using ~describe
annotations. (define-syntax (define-renderer stx)
(syntax-parse stx
[(_ {~describe "function header"
(FN-NAME:id ARGS:id ...)}
{~describe "key-value list"
({~seq KEY:keyword VALUE:expr} ...)}
{~describe "function body"
FN-BODY:expr} ...+)
#'(define (FN-NAME ARGS ...)
(hash 'function (λ ()
FN-BODY ...)
{~@ (keyword->symbol 'KEY) VALUE} ...))]))