samth
2021-5-13 13:28:02

Fantastic!


jestarray
2021-5-13 15:40:55

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


samth
2021-5-13 15:42:46

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.


samth
2021-5-13 15:43:21

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


massung
2021-5-13 15:44:50

yeah, not sure where you saw that code, but I’ve done that several times myself. Sadly, there’s actually multiple issues, though.

  1. The argument is only evaluated once (the obvious problem), so it’s the same vector.
  2. 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).

massung
2021-5-13 15:45:26

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


massung
2021-5-13 15:45:42

(that’s on 8.1 [cs])


samth
2021-5-13 15:46:26

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


massung
2021-5-13 15:47:35

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.


massung
2021-5-13 15:49:01

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


kyp0717
2021-5-13 16:36:22

Thanks all for your help!


kyp0717
2021-5-13 16:37:37

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


kartiksabharwal7
2021-5-13 16:44:44

@kartiksabharwal7 has joined the channel


hj93
2021-5-13 17:06:11

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?


massung
2021-5-13 17:15:02

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.


massung
2021-5-13 17:16:11

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.


hj93
2021-5-13 17:18:49

Mhmm ok thanks


javier123mendoza
2021-5-13 18:34:27

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> ; =&gt; ID (<http://google.com>) ; instead of (http:// <http://google.com\|google.com>)


javier123mendoza
2021-5-13 18:37:08

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.


samth
2021-5-13 18:39:07

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


sschwarzer
2021-5-13 19:16:02

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: #&lt;eof&gt; Is this a known problem? (I can’t find anything in the ticket system, but I guess this is difficult to search for.)


kellysmith12.21
2021-5-13 19:22:16

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.


samth
2021-5-13 19:22:57

mflatt
2021-5-13 19:23:44

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


sschwarzer
2021-5-13 19:26:28

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


sschwarzer
2021-5-13 19:28:42

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


mflatt
2021-5-13 19:32:04

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


sschwarzer
2021-5-13 19:32:35

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


samth
2021-5-13 19:33:00

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


sschwarzer
2021-5-13 19:33:53

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


sschwarzer
2021-5-13 19:34:40

@mflatt Should I enter a bug report?


mflatt
2021-5-13 19:34:48

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


mflatt
2021-5-13 19:35:14

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


mflatt
2021-5-13 19:36:12

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


sschwarzer
2021-5-13 19:42:04

sschwarzer
2021-5-13 19:43:15

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


sschwarzer
2021-5-13 19:47:36

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


sschwarzer
2021-5-13 19:49:41

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


mflatt
2021-5-13 19:59:24

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


mflatt
2021-5-13 19:59:55

(I did change the default for Windows)


sschwarzer
2021-5-13 20:06:42

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


sschwarzer
2021-5-13 20:07:57

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


massung
2021-5-13 20:24:21

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, #, #&amp;, #', 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:


sschwarzer
2021-5-13 20:27:26

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?


kellysmith12.21
2021-5-13 20:45:07

Regarding all the macro-building forms (syntax-rules, syntax-case, syntax-parse; and the templates syntax/#' and quasisyntax/#) they are all just macros built usingdefine-syntax,syntax-quote, andsyntax-local-value. There’s also an implementation ofdefmacro, if you want to use it. Also,syntax-rulesandsyntax-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 theturnstile` 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.


jagen315
2021-5-13 21:18:46

> 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…


dan.ml.901
2021-5-13 21:49:05

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.


massung
2021-5-13 21:56:54

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.


jaz
2021-5-13 22:02:30

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


kellysmith12.21
2021-5-13 22:11:22

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-&gt;list #'(var ...))) "duplicate variable name in let-form" #'((λ (var ...) body) val ...)])


kellysmith12.21
2021-5-13 22:14:50

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


dan.ml.901
2021-5-13 23:03:04

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


dan.ml.901
2021-5-13 23:03:06

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


dan.ml.901
2021-5-13 23:03:17

The error points to line 4, the lambda


dan.ml.901
2021-5-13 23:03:44

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


jaz
2021-5-14 00:44:49

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


jaz
2021-5-14 00:45:55

Same in 8.0.


hazel
2021-5-14 00:59:28

so I have this macro: (define (keyword-&gt;symbol s) (string-&gt;symbol (keyword-&gt;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-&gt;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) ; =&gt; (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?


hazel
2021-5-14 01:00:49

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


hazel
2021-5-14 01:01:05

preferably there’d be a nicer syntax error


hazel
2021-5-14 01:02:51

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.


hazel
2021-5-14 01:05:21

(or coming up with a better syntactic form.)


kellysmith12.21
2021-5-14 01:06:32

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.


kellysmith12.21
2021-5-14 01:07:51

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


hazel
2021-5-14 01:09:10

thank you very much!


kellysmith12.21
2021-5-14 01:10:02

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


kellysmith12.21
2021-5-14 01:15:29

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-&gt;symbol 'KEY) VALUE} ...))]))


kellysmith12.21
2021-5-14 01:17:14

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.


capfredf
2021-5-14 01:19:15

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


hazel
2021-5-14 01:19:28

no it’s zero or more


hazel
2021-5-14 01:19:31

much appreciated


hazel
2021-5-14 01:19:45

my macro abilities are limited


kellysmith12.21
2021-5-14 01:23:23

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


capfredf
2021-5-14 01:24:36

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


kellysmith12.21
2021-5-14 01:26:31

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


kellysmith12.21
2021-5-14 01:37:02

@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?


samth
2021-5-14 01:48:56

@jaz is that with debugging on in DrRacket?


dan.ml.901
2021-5-14 01:53:31

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


dan.ml.901
2021-5-14 01:53:42

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


samth
2021-5-14 01:54:18

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


dan.ml.901
2021-5-14 01:55:49

huh… ok that’s a reason to switch then


jaz
2021-5-14 01:57:06

@samth Yeah, that’s with debugging on


jaz
2021-5-14 01:58:12

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


dan.ml.901
2021-5-14 01:59:01

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


samth
2021-5-14 01:59:35

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


jestarray
2021-5-14 02:24:09

should i report this ?


jestarray
2021-5-14 02:24:29

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


samth
2021-5-14 02:24:48

yes


jestarray
2021-5-14 02:26:03

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


hazel
2021-5-14 02:37:38

I did not no


hazel
2021-5-14 02:37:52

I just wanted it to be syntactically invalid to omit it


hazel
2021-5-14 02:37:55

instead of failing in an unexpected way


sorawee
2021-5-14 02:43:59

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-&gt;string (syntax-e #'x)))] #:when (string-prefix? s "http://") #:do [(define s* (string-&gt;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>)


kellysmith12.21
2021-5-14 03:34:42

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-&gt;symbol 'KEY) VALUE} ...))]))


kellysmith12.21
2021-5-14 03:39:30

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-&gt;symbol 'KEY) VALUE} ...))]))