
Well I’ve never used ClojureScript, but I guess yes, it’s similar to some kind of “transpiling”. Bascially it could look like this: (javascript
(function (hello who)
(console.log (+ "Hello " who "!")))
(hello "world"))
and this would generate: function hello(who) {
console.log("Hello " + who + "!");
}
hello("world");

So that programmers can have the power of Racket macros at hand when writing javascript. Or even generate javascript on-the-fly in a web server.

Can someone briefly explain how define-syntax, syntax-parse, syntax-rules, syntax-case relate to each other?

https://www.greghendershott.com/fear-of-macros/ should cover all of these, but to give you a summary
Historically, syntax-rules
comes before syntax-case
, and syntax-case
comes before syntax-parse
. Regarding expressiveness, syntax-rules
is less expressive than syntax-case
, and syntax-case
is less expressive than syntax-parse
.
syntax-rules
= traditional hygienic syntax matcher in Scheme syntax-case
= an extension of syntax-rules
so that you can manipulate syntax object too, allowing you to break hygiene if you want syntax-parse
= syntax-case
with better error message and with other convenient utilities.

syntax-{rules, case, parse}
are pretty much a match
working on syntax objects. You can think of it as match
/case
/if
.
define-syntax
lifts a regular function that consumes a syntax object and returns a syntax object to macro.

Yeah, I was reading fear-of-macros, transform! section, but felt I was missing the overall picture.

summarizing high levels: by invention date (increasing): rules, case, parse by expressive power (increasing): rules, case, parse jump from rules -> case: ‘syntax object’, can break hygiene jump from case -> parse: better error msgs Is that correct?

yes

Great, thanks for clarifying!

In https://www.greghendershott.com/fear-of-macros/Transform_.html <— is this rules, case, parse … or manually writing transforms ?

That’s a manual transformer

(define-syntax (say-hi stx)
#'(displayln "hi"))
looks awfully similar to clojure style defmacros

Yup, that’s correct

Is the following intuition correct: define-syntax = write macros as “sexp -> sexp transforms” rules / case / parse = “dsls” built on top of “define-syntax”

I wouldn’t say “on top of define-syntax”. They are orthogonal

rules / case / parse are just pattern matchers, just like match
. However, they work on syntax objects instead.

E.g.,
(syntax-parse #'(hello world)
[(id1 id2) #'(id2 id1)])
;=> #'(world hello)
The above code roughly compiles to:
(define stx #'(hello world))
(define datum (syntax->datum stx))
(define new-datum
(if (= (length datum) 2)
(list (second datum) (first datum))
(error 'expect-length-2)))
(datum->syntax stx new-datum)

sounds like I need to go write some rules/case/parse to even get the right intuition :slightly_smiling_face:

If we squint, is syntax-parse basically (1) pattern-match/unification + (2) rewrite rule ?

(compiled to ifs/conds)

Correct

Thanks, this is finally starting to make sense, for some reason, syntax-rules/case/parse always seemed like this big pile of black magic.

I have an unrelated question: do you use dr racket? Coming from an Emacs/IntelliJ-IDEA background, dr racket feels like notepad right now — not because of dr racket is limited, but because I’m not familiar with the key bindings / workflow. Any advice on reaching working proficienty in dr racket?

Let me try this one more time
You can use match
on values:
(match (list 1 2)
[(list a b) (list b a)]) ;=> (list 2 1)
syntax-{rules, case, parse}
is like match
, but it works only on syntax objects.
(syntax-parse #'(1 2)
[(a b) #'(b a)]) ;=> #'(2 1)
The above two computations happen at runtime.
We can lift a function that consumes a syntax object and produce a syntax object to compile time as a macro.
(define-syntax (foo stx)
(match (syntax->datum stx)
[(list _ x y) (datum->syntax stx (list y x))]))
(foo 10 add1) ;=> transform to (add1 10) ;=> 11
(define-syntax (bar stx)
(syntax-parse stx
[(_ x y) #'(y x)]))
(bar 10 add1) ;=> transform to (add1 10) ;=> 11

What is the distinction between “values” and “syntax objects”? I was recently just writing a scheme interpreter, is the following correct:
values = some VM object that exists at run time syntax object = refers to some continuous substring (may be multi line) of some buffer, that also happens to be parseable scheme expr ?

syntax object is a kind of value

hmm, let me try this again value = set of all objects the VM can create syntax object = needs a buffer-name, starting column, starting row, length Is that correct? If not, I’m missing something fundamental

I’m not familiar with the terminology that you are using, but it sounds correct to me. My point was that syntax object is also a regular value.

But not the other way

I’m new to racket, so probably using the wrong terminology. Let’s play “pretend to be the compiler” — suppose we want to compile + run main.rkt … is the following approximately correct:
- compile main.rkt:
- racket fires up a racket VM, goes through main.rkt, finds all define-syntax; for each macro, we add a function to the VM
- we run through file again, for each macro, calling corresponding function to expand the sub expressionN
- we now have a racket program w/ no macros
- we compile the code
- now, at runtime:
- app fires up a different racket vm
- interprets out compiled bytecode point being: a racket vm is fired up during the compile time for the macro processing Is the above approximately correct?

so macros are racket functions executed on a racket VM — but a VM that exists only at compile time — i.e. not the VM run at runtime of the app

Correct.

Thanks, and except in cases where we do
(define stx_blah #'(....))
except in the above cases, syntax-objects exist on the compile-time-macro-expansion-VM, and not the runtime-VM (unless we explicitly create one via (syntax ...)
)

^ Is that also correct regarding syntax objects?

Syntax object can exist in both runtime and compile time

Hmm, I phrased that poorly.

syntax-objects created via macro-expansion exists only on compile-time-VM syntax-objects created via (syntax ...
and #'(...)
exist at runtime ^— is this correct? :slightly_smiling_face:

Almost, but it doesn’t depend on (syntax ...)
or #'(...)
. Instead, it depends on (define-syntax ...)

#'(a)
will be used in macro expansion (compile time) when it’s in (define-syntax ...)
. And #'(a)
will be a runtime value when it’s not in (define-syntax ...)

I see, so perhaps it should be stated as:
(syntax ...)
and#'(...)
create syntax objects- they are normal VM objects and created whenever they are executed
(define-syntax ...)
creates a function that is executed on compile-time-VM- syntax-objects exist on whatever VM they’re created on [tautology, lol]

Correct!

Yeah, sorry, I communicated very poorly. Glad that you finally figure it out though.

You communicated great; I didn’t have the right terms / mental models.

I figured out why I’m so unhappy with Dr Racket — paredit — does Dr Racket have paredit key bindings?

I don’t use DrRacket anymore, tbh. Have been using Racket mode in Emacs


Well, thanks for all your time/patience in explaining the gist of the macro system ot me.

I really appreciate it.

I’m signing off for now, have a nice day!

is a define-syntax-rule basicalyl a define-syntax/syntax-case that only has one ‘pattern matching line’ ?

There’s also define-simple-macro
in syntax/parse/define

But all of these are simply macros themselves. They are not primitives


Honestly, I sometimes wish @greg will rewrite his macro tutorial to avoid syntax-case
entirely (since it’s simply syntax-parse
without syntax class), and by the same analogy, replace define-syntax-rule
with define-simple-macro
.

Sounds like a cool idea to me. JavaScript is such a versatile language having a way to tap into its ecosystem would extend Racket’s reach quite a bit.

@leif you’re listed as a manager of the racket project in openhub. Do you know why commits haven’t been updated since 2017? https://www.openhub.net/p/racket

Beautiful Racket is great for DSLs. However, are there any examples of DSL -> other language, i.e. a “transpiler” of sorts.

@todo yes, people have built some for generating c++ of all things






@samth @githree: nice, thanks!

@stoic0 has joined the channel

@pocmatos TIL I manage the racket project on openhub…lol.

(Maybe I signed up for it a few years back?)

Anyway…

You show up as one of the managers. The other one is Eli but I haven’t seen him here before.

Ah, I think I found it.

Eli used to be very involved. Less so these days.

I have no permissions so can’t check but it would be great to get the data up to date.

Yup, it looks like all of the code locations for racket got removed.

Umm…do you know of a better way to add them (besides going through each repo in http://github.com/racket individually)

OH wait, you can just tell it to track a github user.

I’ve added it now.

So….hopefully it will update?

Looks like it should update in the next 24 hours or so. Does that work for you @pocmatos?

TY i will try it

Will check, not home at the moment.

@stamourv Looking at draw-arrow
in racket/draw/arrow
(which it looks like you added)

Can you tell me why you added a required dx and dy argument? (Especially given that, say, draw-line
doesn’t have it.)

It also looks like the function draws an arrow from ((startx + dx), (starty + dy)) to ((endx + dx), (endy + dy)). If that’s correct, maybe it would make sense to reflect that in the docs?

Did I add that? I honestly have no memory of that.

Ah, lol, fair enough.

Ah, looking at the git logs, I moved it from unstable/arrow. I just took what was there.

AH!!! okay

(I just ran a git blame, and it blamed you.

But that makes more sense.

So you may want to see who wrote it in that repo.

Will do.

I take it then that you wouldn’t mind if the dx and dy became optional arguments.

(To bring it more inline with the rest of the racket/draw API, while also not breaking backwards compatibility.)

That sounds reasonable to me, but I’m not the right person to ask.

Ah, okay. Looks like @samth Is the only other person to have touched it: https://github.com/racket/unstable/blob/master/unstable-lib/arrow.rkt

Anyway, thanks.

Err…or not. I guess sam was only removing directories: https://github.com/racket/unstable/commit/7e1fd09014ab9a52d6ed6c4623438e6dac9e0734#diff-bbb45214b09c8dd88ce47e89fe440d74

draw-arrow is very old and I think was created by @robby



Well this got out of hand quickly :smile: https://twitter.com/mattmight/status/1132448468994330625

https://docs.racket-lang.org/reference/file-ports.html#%28def._%28%28lib._racket%2Fprivate%2Fbase..rkt%29._with-output-to-file%29%29 how do I fix: (with-output-to-file "blah.tex"
(lambda () (printf "hello world"))
[#:exists 'replace])

@todo Just remove the square brackets.

In Racket docs, the square brackets indicate optional arguments. You don’t actually supply the square brackets.

Also, keyword arguments — like #:exists 'replace
here — may go anywhere among the positional arguments. So, how you have it above is fine (without the square brackets). Or, for instance, you could also do (with-output-to-file "blah.tex" #:exists 'replace
(lambda () (printf "hello world")))

Or (with-output-to-file "blah.tex"
#:exists 'replace
(lambda () (printf "hello world")))

@leif: what was the actual question? That function draws arrows in check syntax and it was refactored out like that so Philippe could use it in MrFlow

More generally many GUI library functions take dx/d’y arguments because other functions pass them. Like the draw method of a snip or of a canvas.

@greg: got it working, thanks!

Is there a way to run a command FROM A PARTICULAR DIRECTORY?
I’m currently using pdf-latex
from the slatex package. However, it’s duping all the intermediate files in cwd instead of the directory where the tex file is located.
I am currently looking at https://docs.racket-lang.org/reference/subprocess.html but I don’t see a way to specify the working directory.
In particular, what I want to do is to
run “pdflatex blah.tex” from directory "/home/foobar/tmp/"

Try (parameterize ([current-directory PATH-TO-YOUR-DIRECTORY]) (YOUR-COMMAND-HERE))
?

is “parameterize” like “dynamically scoped vars”, or is it something else?

okay, it definitely worked

@robby Eh, while not super important, the original question was to make the dx/dy arguments optional.

(defaulting to 0), that way it could be more in line with, say, the draw-line
method.

Is there a reason to change the code?

Not a big one no.

Maybe best to leave it alone, then.

SMall one: it got annoying for me to put 0 0
at the end of several function calls. :wink:

But that’s admittedly small.

But it might make sense to update the docs to clarify where the line is drawn. :wink:

I’ll do that later…. (famous last words)

Docs update would be nice

On the subject of arrows, I’m trying to use the check-syntax button to draw arrows. These arrows are being used for metadata annotations, associating an element from a tag with some syntax in the function body. I’ve tried to read through the code in drracket/private/syncheck/gui.rkt, and I think that it should be possible to create an arrow from an arbitrary location to another arbitrary location. Is it possible to do that without extending the check-syntax tool? An example would be drawing an arrow between one-of
and the open-paren before cond
in the following: (@dd-template-rules one-of atomic-distinct atomic-distinct)
(define (fn-for-foo f)
(cond [(string=? "L" f) (...)]
[(string=? "R" f) (...)]))

Yes. The way I’ve used to draw arrows between arbitrary locations is to generate a fresh identifier, make copies with different source locations, and use 'disappeared-use
with one source location and 'disappeared-binding
with the other source location

Thanks.

I’ll look into this.

I’m not quite sure how to work that, here’s what I have: (require (for-syntax racket/syntax))
(define-syntax (tag-it stx)
(syntax-case stx ()
[(_ a b)
(let ([temp (generate-temporary)])
(begin (syntax-property #'a 'disappeared-use temp)
(syntax-property #'b 'disappeared-binding temp)))]))
(define a 1)
(define b 2)
(tag-it a b)

I expect an arrow from b
to a
in tag-it
, but I’m not getting anything.

Here’s what I mean below. You still need the 'orig-for-check-syntax
property on the source-location identifiers #lang racket
(require (for-syntax racket/syntax))
;; from up here
(begin-for-syntax
(define (orig stx)
(syntax-property stx 'original-for-check-syntax #t))
(define (add-arrow stx from to)
(define id (orig (generate-temporary)))
(define sym (syntax-e id))
(syntax-property
(syntax-property
stx
'disappeared-binding (datum->syntax id sym from id))
'disappeared-use (datum->syntax id sym to id))))
(define-syntax (draw-arrow stx)
(add-arrow #'(void)
(list (syntax-source stx) #f #f 61 7)
(list (syntax-source stx) #f #f 609 14)))
;; to over down here
(draw-arrow)

How do I annotate / export this untyped function so I can call it from typed racket?
#lang typed/racket
(require slatex/slatex-wrapper)
(define (write-pdf dir-name file-name content)
(parameterize ([current-directory dir-name])
(with-output-to-file file-name
(lambda ()
(write-string "\\documentclass{article}\n\\usepackage{fullpage}\\begin{document}\n")
(write-string "\\begin{displaymath}\n")
(content)
(write-string "\\end{displaymath}\n")
(write-string "\\end{document}")
)
#:exists 'replace)
(pdf-latex file-name)
))
(write-pdf "/data/ftp/tmp" "blah.tex"
(lambda ()
"x = y + 1"))

Thanks. This works nicely.

What type would you give it in the typed world?

String, String, Function -> ()

wait so content
is a function but what does it return?

it doesn’t return anything, the key is that the with-output-to-file hijacks stdout

So when you call it it returns a string?

oh sorry, I passed a bad content function, 1 sec

oh okay

(write-pdf "/data/ftp/tmp" "blah.tex"
(lambda ()
(write-string
"x = y + 1")))

content doesn’t return anything; it writes to stdout, which is then captured by the with-output-to-file

(String, String, () -> ()) -> () would be the type sig I want for write-pdf

So that looks like it would have the type (-> String String (-> Void) Void)

Sounds right.

So do I need to create a separate file with #lang racket … or can we, in the middle of a #lang typed/racket file, say:
for this next function, it’s going to be untyped ?

In the untyped file, provide it normally. In the typed file, use require/typed
with the type above

If this is not too much trouble, can you write the #lang typed/racket file for me? I think it’ll be alot easier to understand how all the pieces fit together from a working example.

I’m trying #lang typed/racket
(require/typed "util.rkt"
[write-pdf (-> String String (-> Void))])

and getting error:
. only-in: identifier `write-pdf' not included in nested require spec in: "util.rkt"

it all works now; thanks!

For racket exact-num/big-int, is there a O(1) way to get # of bits used? I’m building a http accessible simple calculator and need to avoid DOS by using large nums to consume all memory.

integer-length

I know about https://docs.racket-lang.org/nanopass/index.html
Question: where can I find the actual assignments that goes along with this framework?

Is it normal in Dr Racket for the following code to take seconds to compile ?
(define-type MathNode (U BigFloat MathNeg))
(struct BigFloat ([base : Number] [exp : Number]))
(struct MathNeg ([base : MathNode]))
(struct MathAdd ([base : (Listof MathNode)]))
(struct MathMul ([base : (Listof MathNode) ]))
I expect typed racket to be slightly slower but not this much slower.

#lang racket
(require Racket-miniKanren/miniKanren/mk)
(define fail
(== #t #f))
(define succeed
(== #t #t))
(current-readtable
(make-readtable (current-readtable)
#\s
'dispatch-macro
(lambda (a b c d e f) succeed)))
(current-readtable
(make-readtable (current-readtable)
#\u
'dispatch-macro
(lambda (a b c d e f) fail)))
(run* (q) #s)
This results in an error of:
. . read-syntax: expected `(`, `[`, or `{` after `#s`
how do I fix this?

You need to make a language that defines your extended readtable and then use that lang

There is an example at https://docs.racket-lang.org/guide/hash-lang_reader.html you want it to provide a parameterized read
and read-syntax

Parameters like current-readtable
set at run time only affect the run-time evaluation of the code, so they don’t affect compile-time things like reading/parsing the program

However, if you set up a #lang
and set the current-readtable
at “read time” in the lang’s reader module, then it can do that

I’m temporarily using ss for #s and ff for #f.

https://docs.racket-lang.org/syntax-parse-example/index.html <— Does this explain anywhere how repetition, "*" and "…" works? I’m seeing lots of examples but I can’t find an explaination of how these three forms work.

So I now have a file called ‘reader.rkt’ which contains:
#lang racket
(require (for-syntax racket/base syntax/parse))
(define ff
(== #t #f))
(define ss
(== #t #t))
(current-readtable
(make-readtable (current-readtable)
#\s
'dispatch-macro
(lambda (a b c d e f) ss)))
(current-readtable
(make-readtable (current-readtable)
#\u
'dispatch-macro
(lambda (a b c d e f) ff)))

In this code, why can’t I export my macros and how can I fix it:
#lang racket
(provide (for-syntax conj disj)
ff ss
)
(require (for-syntax racket/base syntax/parse))
(require Racket-miniKanren/miniKanren/mk)
(define ff
(== #t #f))
(define ss
(== #t #t))
(define-syntax (disj stx)
(syntax-parse stx
[(_) #'ff]
[(_ clause) #'clause]
[(_ clause ...) #'(conde (clause) ...)]
))
(define-syntax (conj stx)
(syntax-parse stx
[(_) #'ss]
[(_ clause) #'clause]
[(_ clause ...) #'(conde (clause ...))]))

@nryoung has joined the channel

@todo Try using (provide conj disj)
instead of (provide (for-syntax conj disj))
See: https://docs.racket-lang.org/reference/syntax-model.html?q=define%2Dsyntax#%28part._transformer-model%29 “In a top-level context or module context, when the expander encounters a define-syntaxes form, the binding that it introduces for the defined identifiers is a transformer binding. The value of the binding exists at expansion time, rather than run time (though the two times can overlap), though the binding itself is introduced with phase level 0 (i.e., in the base environment).”