Because it expands to code that increment the count when the module is being instantiated at phase 1, rather than incrementing the count at the time a module is compiled
On the contrary, I’m not sure if that can work in internal definition contexts.
(^ due to the use of begin-for-synta
)
btw the example in You Want it When is like this. Same for the type system implementation in Languages as Libraries.
I guess the modern implementation of struct
is fancier, though.
Conveniently, I only need it to work at the module level, since module+
can’t appear in an internal definition context anyway.
So this is perfect. Thanks again :slightly_smiling_face:
Yeah, I need to reread those papers.
haha, I’ve found a solution - https://docs.racket-lang.org/natural-cli/index.html\|https://docs.racket-lang.org/natural-cli/index.html :) it wraps cmdline in a very nice way
I have a question (or several) about startup time. When I run #lang racket/base
"Hello world"
with $ time racket hello.rkt
, I get about 145 ms runtime. Translation with raco make
results in a runtime of 125 ms. raco exe
with $ time ./hello
gives about 130 ms.
With #lang racket
, the runtimes are 380 ms, 290 ms and 335 ms, respectively.
(For comparison: On the same computer, Nim, Go and OCaml take about 5 ms for a “hello world” program, compiled Chicken Scheme takes 10 ms, Python takes 55 ms.)
My understanding/assumption is that raco make
and raco exe
processing does all the macro expansion and most of the code is compiled to machine code (with Racket 8.0). Why does the program still take that long? What does Racket spend the time on? Probably related: Why are the startup/runtimes with #lang racket
so much longer than with #lang racket/base
, even if I don’t use the additional functionality of #lang racket
over #lang racket/base
?
You can shave a bit more time if you use #lang racket/kernel
, but if you are serious about the speed of your hello world program, you need to use assembly: https://jameshfisher.com/2018/03/10/linux-assembly-hello-world/
No, I’m not that serious about the runtime. :smile:
More seriously though, I dislike “having” to think about using a feature because of how the additional require
will increase the runtime. This is rather pronounced if you use #lang racket/base
, but require racket/match
.
So my goal isn’t to reduce the runtime at all costs, but to reduce the runtime without reducing the used language/library features a lot.
What I’d wish for would be a “minimal” startup time of ≤ 50 ms. Ideally, a mere require
shouldn’t increase the runtime at all (unless the module has to do some expensive initialization).
Anyway, although I keep telling me that the current Racket runtimes should be fine, I come back to the question why Racket is relatively slow (despite that execution times, apart from the startup, are usually pretty good).
Nice!
Yes, in general side effects in macros are usually better as side effects in begin-for-syntax in the expansion of macros
On the pattern matching duplication question, I think there’s 1. match 2. a collection of sexp/syntax matchers (syntax-case, syntax-parse, datum-case, one in the expander) 3. redex
They use pretty different implementation strategies and have fairly different constraints, but also it’s a lot of different ones so it seems like something could be unified
I get an error unrecognized parsed form: (parsed-define-syntaxes ...)
at the REPL, definitely due to how top-level being hopeless. Is this considered a bug (it looks like an internal error to me) and should I report the issue?
That’s all fine. The only reason not to bind static information to a name is if you want to use it with something like match
, struct-out
, or shared
. Specifically, the only effect of #:omit-define-syntaxes
is to not create a structure type transformer binding: https://docs.racket-lang.org/reference/structinfo.html
yeah that doesn’t seem like an error your should see
(Though I wonder if you might want to call the struct group-param
for a single param.)
Unfortunately, this results in 2
. Ideally I want it to be 1
.
#lang racket
(module defn racket
(provide mac get-mac)
(define-for-syntax counter 0)
(define-for-syntax (inc!)
(set! counter (add1 counter)))
(define-syntax (mac stx)
#'(begin-for-syntax
(inc!)))
(define-syntax (get-mac stx)
#`'#,counter))
(module foo racket
(require (submod ".." defn))
(mac))
(require 'defn 'foo)
(mac)
(get-mac)
That is, I only want “leaking” to occur for module+
, but not across modules in general.
@philip.mcgrath I don’t understand everything under that link, but I think I have a better idea now what’s going on. :slightly_smiling_face:
Regarding the name group-params
, it means multiple params (represented by the fields) for a single group. On the other hand, it may be an option to name the hash groups-params
because the hash contains params for several groups.
Besides, now I wonder if it’s more common in Racket code to use singular or plural for the names of hashes (singular as in: “if you use the hash and hash-ref
, you get a single value back” or plural as in: “this hash contains multiple values”).
For the record, if I use #lang racket/base
and require racket/match
, the runtimes (uncompiled, raco make
, raco exe
) are 290, 230 and 270 ms, respectively. This is about twice the time as for the program without (require racket/match)
.
There are a variety of things that contribute to startup time. The Racket binary takes about 55 ms to start up. Then loading the expander takes some time, even though that’s just creating some top-level values. Then loading racket/base
involves substantial IO, plus executing the top-level of a bunch of files, even though that mostly just creates functions, and some of it happens at multiple phases (about 120 linklets get instantiated to start racket/base
). Then loading more modules just takes more time. Also, the serialized form of compiled code has to be read in and turned into values, which is fast but not instant in all of these processes.
Why do you want this semantics?
module+
is just a macro, so I don’t know exactly what semantics you do want.
You might find the output you get if you try: PLT_LINKLET_TIMES=1 racket -l racket
interesting.
@sschwarzer If I recall correctly, much of the startup time is due to i/o. I remember @mflatt at some point thought about packaging the needed files into a single file (zipped?), thereby reducing the number of files to access. I don’t know, if this idea was implemented.
@samth Yes, that’s very interesting, thank you. Also the different statistics for racket/base vs. racket.
significantly faster startup would require probably some combination of: - being able to mmap
in serialized compiled code - being able to determine that modules are pure statically so that less execution has to happen at startup - creating tools that flatten programs into single files that can then be compiled into the binary
I had thought/hoped that raco exe
would do that.
@samth What does “faslin” mean?
that’s deserializing from the “fasl” formalt
Well, I want to simulate a scope of a variable in a module context. So:
#lang racket
(module foo racket
(define-syntax x ...)
(module+ test
;; x is in scope here
))
(require 'foo)
;; but not here, since it's not provided
Maybe you want syntax parameters?
Not sure how syntax parameters will help. What I’m providing are simply mac
and get-mac
(in the above example — or define-mixfix
and #%app
for the mixfix library). It doesn’t look like I have anything to syntax-parameterize
over in the first place, let alone the meaning of the syntax parameters, etc.
Ah, ok. In that case I think define-mixfix
should bind an identifier, and maintain a hash table mapping that identifier to whatever, and then #%app
should look it up. Just like types work.
And then scope is just scope.
@robby Not urgent, but when you have a chance, this intersects two of your areas, contracts and drracket. 1. Given some function foo
, contract-out
defines a wrapper function named provide/contract-id-foo.1
, and, exports that renamed as “foo”. As a result, provide/contract-id-foo.1
is the source identifier reported by identifier-binding
(and therefore syncheck:add-jump-to-definition
). 2. Currently check-syntax
does not call syncheck:add-definition-target
for that wrapper definition provide/contract-id-foo.1
. What if it did — pointing to the location of (say) foo
in the contract-out
form. Does that seem correct to you, to start doing that?
If so I think I’d like to make a PR to add that. It would simplify jumping to definitions.
(To-date, Racket Mode has tried to be clever about jumping directly to foo
, transitively, “through” the contract wrapper. But I’ve been working on similar for a “database” representation, and I’m realizing it’s better to be “honest” about the reality that there are two separate definitions, and store them as such. Any transitive jumping should be a higher-level thing if desired.)
Context: I’m sharing more for the comments but the small amount of code might be useful too idk: https://gist.github.com/greghendershott/eccdb9b847430889e7f992842e13a849#file-pdb-rkt-L117-L239
re the parenthetical: that’s the decision that check syntax has been taking. I think that’s useful in the contract case as you “pass by” the actual definition of the contract and having a look might well be what the programmer wanted to do.
There is a raco demod
but (a) I think it might not work in Racket CS and (b) I think it’s motivation was more about whole-program optimization opportunities than reducing I/O for load times?
All the "I think"s in that sentence mean that I don’t really know what I’m talking about, but someone else might. :slightly_smiling_face:
I don’t know if I’m fully understanding your proposed change but doing something that somehow takes into account the names that the contract system uses in contract-out doesn’t seem right.
If we really felt we needed to know something about the macro we should define a protocol and the change the contract system to follow it.
(eg some property or something that the macro could put into the expanded thing)
There are probably other things that would also benefit from following the protocol and we could document it, etc.
Also “it’s” => “its”.
Not sure if I understand your comment. define-mixfix(-rule)
doesn’t create any binding for users to use. It simply records the syntax transformer. It’s #%app
’s job to walk through all syntax transformers to perform a transformation. Here’s an example of its usage:
(define-mixfix-rule (a #:+ b)
(+ a b))
(#%app 1 #:+ 2) ; expands to (+ 1 2)
It’s true that internally, define-mixfix-rule
creates a binding and record this binding in a data structure. The crux of the issue, then, is that when #%app
walks over the data structure to find a syntax transformer to use, it will see leaked bindings from other require
d modules, assuming that define-mixfix-rule
compiles to begin-for-syntax
. But if define-mixfix-rule
doesn’t compile to begin-for-syntax
, #%app
in module+
won’t see the data structure.
I think my answer is that you want the connection between define-mixfix-rule
and #%app
to go via a binding, so instead of just walking the hash table, you look up that binding.
I see what you mean now. Unfortunately, the whole point of mixfix is that there’s no single binding that is responsible for carrying the information, unlike regular macros. So while your suggestion would certainly work, it will defeat the whole purpose of the library.
Racket and OCaml seem to be awfully similar - they’re both mostly functional, they both have (underused) object systems, delimited continuations, good ways to extend the language (macros and extension points). TIL Racket even has a module system, just like OCaml.
what unique advantages does Racket have? from the “making stuff in it” perspective, not from the educational/research/creating new languages perspective (as you can’t really compete with Racket in these fields)
Why not put a binding on \|#:+\|
or something like that?
In general, I think you either need to do what I’m suggesting, or take over #%module-begin
to implement module scoping yourself.
Some unique aspects of the language, relative to OCaml: - concurrency control with threads and events - in-process control of resources, with eventspaces and custodians - delimited continuations (I think the Racket support is in a different category than the OCaml support) - dynamic evaluation (with eval
and friends) - an extensive and high-quality standard library, including lots and lots of cross-platform graphics support
thanks @samth! I guess Racket’s eval is different than others? I’ve been always told to avoid eval like the plague :)
No, you should definitely avoid eval
. But there are some situations where eval
is needed; for example DrRacket calls eval
on your code.
oh I see. and with sandboxes stuff is a lot safer, isn’t it?
yes, that’s what I mean — lots of languages have eval
but Racket has high-quality support for it.
got it! thanks for explaining, I really appreciate it :)
What check-syntax reports (via identifier-binding) is the identifier that contract-out
made up, e.g. provide/contract-id-foo.1
. So I think that horse has already escaped the barn?
I think the challenge is 1. how to find the horse (provide/contract-id-foo.1
is nowhere in the expanded code! — provide/contract-id-foo
is) and then 2. how to make that horse lead us to foo
. :)
I like the idea of an official protocol.
For 2 it seems easy enough to have some syntax-property saying “I am the wrapper for foo
”?
I’m not sure what to do about 1 (the extra ".1"
suffix ) because I don’t understand the mechanism or rationale for that. If you have some idea that would be great!
It’s just very weird now because normally when identifier-binding
reports that an identifier is defined somewhere, sure enough it is there in the expanded syntax among the define-values
or define-syntaxes
forms (in my limited experience).
I seem to remember that OCaml gui was based on tk 15 years ago. It has likely evolved since then, but that wasn’t a great experience at the time. OCaml is a very nice language overall though.
(In the past I’ve tried to work around this by trying to find things using the nominal-id
reported by identifier-binding
, or even trying to walk non-expanded code looking for contract forms. But I was hoping to cast away all such hacks and heuristics forever.)
In that gist I simply have a more focused and barely more principled hack. :slightly_smiling_face: So it’s progress but a real protocol is definitely best.
TL;DR that gist has both (a) a hack that should be changed into an official PR/change for racket/contract
and (b) a non-hack that I think would be a good for PR for drracket/check-syntax
, which is to call syncheck:add-definition-target
the definitions of contract-out
wrappers discovered via (a). I think that’s a better summary of what I’m proposing.
Maybe I should open a PR for drracket/check-syntax
? The discussion there will probably lead to you suggesting I move the hacky parts into a PR for racket/contract
in the form of a real protocol. That might be easier than me Slack-ing too verbosely?
I think OCaml does not come with a standard GUI library the way Racket does.
Too bad. It’s certainly a lot of work though, and I’m very grateful that racket has one!
most people seem to be using this for native GUI in OCaml https://garrigue.github.io/lablgtk/\|https://garrigue.github.io/lablgtk/
and a lot of people use standard library extensions/replacements, there are at least three popular ones (Batteries, Jane Street’s Core, Containers)