Hi, I have a question about https://docs.racket-lang.org/guide/phases.html?q=phase#%28tech._phase%29 and how macros affect the phase.
Suppose I have the following code where I define the binding to three
.
(define three 3)
three
This works as expected. We can shift the phases by doing, begin-for-syntax
- which paraphrasing the documentation means shifting the phase up by one. Wrapping the two statements separately in a begin-for-syntax
block works as expected.
If I now define a macro which produces an expr
with a define
form inside, which phase is the id
available in? I had expected that it would be n + 1
of which phase it’s called in but wrapping a three
in a begin-for-syntax
block and calling expand-arb
at the top level gives me an unbound identifier.
(define-syntax (expand-arb stx)
(syntax-parse stx
[(_)
#'(define three 3)]))
Consider this: #lang racket
(require (for-syntax syntax/parse))
(begin-for-syntax
(define three 3))
(define-syntax (expand-arb stx)
(syntax-parse stx
[(_ my-three)
(displayln (list three)) ; you can refer to three directly here
(with-syntax ([three three]) ; use with-syntax if you need to
#'(define my-three three))])) ; refer to three in templates
(expand-arb x)
x
Hmm OK; what you’re saying is that the define
statement doesn’t take a symbol but also the phase information hence the x in this case is x in phase 0?
But what if I introduce a new id
in the macro?
I am saying that (begin-for-syntax
(define three 3))
defines three
at phase 1. But a phase 1 identifier isn’t automatically a pattern variable.
That is expanding to, say, #'(begin three)
won’t work.
Ah, but I’m simply trying to do (begin-for-syntax three)
Which does work with just the define
.
(but not with expand-arb
, hence the question)
To use the value to which three
is bound, one must introduce a pattern variable: (with-syntax ([three three]) #'(begin three))
Here the pattern variable got the same name as the phase 1 variable.
Sorry let me write out the code explicitly
Consider this example: #lang racket
(require (for-syntax syntax/parse))
(begin-for-syntax
(define three 3))
(define-syntax (expand-arb stx)
(syntax-parse stx
[(_)
(displayln (list three)) ; you can refer to three directly here
(with-syntax ([three1 three]) ; use with-syntax if you need to
#'(define three three1))])) ; refer to three in templates
(expand-arb)
three
Here we try to define a phase 0 variable named three
.
However running the example will give an error: “three: unbound identifier”.
This is because the three
introduced by the macro and the three
in the user code has different lexical contexts (but it is unrelated to the phase 1 three).
To fix it we need to give the three
in #'(define three three1)
the same context as the user three
. This is usually done by passing along the identifier.
(x in the previous example)
However we can get the context from stx too. #lang racket
(require (for-syntax syntax/parse racket/syntax))
(begin-for-syntax
(define three 3))
(define-syntax (expand-arb stx)
(syntax-parse stx
[(_)
(displayln (list three))
(with-syntax ([three1 three]
[three (format-id stx "three")])
#'(define three three1))]))
(expand-arb)
three
Ah OK - I had interpreted that everything at the top level shared a lexical context - is that untrue?
In the example we are the module-level, not the top-level.
Hmm - I am clearly missing some more context. I will dig in further in the racket reference. Thank you for the explanation!
The tricky part is that #lang racket ...
expands to a module. So it is easy to forget (I have done that in past).
The definition of top-level and module-level is here: https://docs.racket-lang.org/reference/syntax-model.html?q=fully#%28part._fully-expanded%29
Every time I upgrade Racket, say from version 7.7.x.y to 7.8, I always have the same problem with my local packages, namely: ../../../../Applications/Racket/Racket-v7.8/collects/racket/require-transform.rkt:269:2: standard-module-name-resolver: collection not found
for module path: format-ymd
collection: "format-ymd"
in collection directories:
/Users/gknauth/Library/Racket/7.8/collects
/Applications/Racket/Racket-v7.8/collects
... [175 additional linked and package directories]
no package suggestions are available .
I can never remember the magic incantation / combination of raco commands that will get me back to normal. Can someone remind me? Thanks. Maybe my setup is unusual because I keep multiple different versions of Racket under /Applications/Racket/.
Hmm, just to clarify, the snippet:
#lang racket
(define x 1)
(define y x)
both are bound in the module level context - but both share the same lexical context correct?
I usually just install the missing packages one at a time, like: raco pkg install format-ymd
Is there something better?
And if I am interpreting correctly, you’re saying it’s not because they are both module level + phase level that they share the same context, but some other reason?
% raco pkg install format-ymd
Resolving "format-ymd" via <https://download.racket-lang.org/releases/7.8/catalog/>
Resolving "format-ymd" via <https://pkgs.racket-lang.org>
Resolving "format-ymd" via <https://planet-compats.racket-lang.org>
raco pkg install: cannot find package on catalogs
package: format-ymd
Yes, they have the same context. They are in the same module, and neither are introduced by a macro.
I’m literally in the directory/folder with the source for format-ymd.
Ah OK - then is the explanation simply, macros like functions introduce their own lexical context?
Oh. That was bad example. That must mean that the collection format-ymd is in some other multi collection package (not named format-ymd).
% locate format-ymd
/Users/gknauth/github/format-ymd
Ah! You are not downloading format-ymd.
Then you probably need to use raco link
I see. I’ll look into that, thanks.
@gknauth I have all my Racket source in a folder named GitHub
so with the old version of racket
I can write: raco link -l | grep GitHub
and get a list of all the old links.
Yes. Identifiers introduced by macros have a new lexical context.
Unless one explicitly does something to keep the context from the input.
% raco link -l \| grep github
root path: "/Users/gknauth/github"
% ls
bergy-bits/ itty-bitty/ machine-setup/ pilot-currency/
experiments/ javapjar/ money-matters/ tasks-currency/
format-numbers/ jdkjar/ personal-ledger/ thumbs/
format-ymd/ location-weather/ personal-ledger.tgz weather-bufr/
What more do I have to do, did I do something wrong, do I have to do something differently? I used raco link -d to make the github directory a root. I don’t know if that was enough, if I should have done more.
You can check the beginning of: https://www.youtube.com/watch?v=Or_yKiI3Ha4
By the way @soegaard2, some of that format-numbers stuff originated with you, I remember you had a way to print real numbers that was sane, and I used it.
Funny - can’t remember that.
from 2007–2009: ;; real->scientific-string
;; 2007-04-15 thanks to <mailto:jensaxel@soegaard.net\|jensaxel@soegaard.net>
;; 2009-12-14 modified by gknauth
(define real->scientific-string
(case-lambda
[(x)
(real->scientific-string x 2)]
[(x digits-after-decimal-k)
(let* ([sign (if (negative? x) -1 +1)]
[x (* sign (inexact->exact x))]
[e-safe (if (= x 0)
0
(floor (/ (log x) (log 10))))]
[e-orig (inexact->exact e-safe)]
[e (inexact->exact (- e-safe))]
[x-normalized (* (inexact->exact x) (expt 10 e))])
(format "~a~ae~a"
(if (negative? sign) "-" "")
(if (zero? digits-after-decimal-k)
(round x-normalized)
(real->decimal-string
(exact->inexact x-normalized)
digits-after-decimal-k))
e-orig))]))
;; original format-float came from from Joe Marshall <jmarshall@alum.mit.edu>
;; most of the meat is now in format-numerals
I think I use: cd github
raco link bergy-bits
raco link itty-bitty
...
But raco link *
will probably also work.
Thanks, I got my webapp to run again. raco setup still gave me errors for unrelated things, for example: raco setup: error: during building docs for /Users/gknauth/github/weather-bufr/scribblings/weather-bufr.scrbl
raco setup: standard-module-name-resolver: collection not found
raco setup: for module path: binaryio
raco setup: collection: "binaryio"
raco setup: in collection directories:
raco setup: /Users/gknauth/Library/Racket/7.8/collects
raco setup: /usr/local/racket/latest/collects
raco setup: ... [176 additional linked and package directories]
but that didn’t stop me from building/running the webapp I was working on.
Makes sense (if binaryio is only used to build the docs).
binaryio is used in that weather-bufr program itself, but I’ll figure that out later. The main thing is I got my tasks-currency webapp to work again. There are so many things I have to keep track off, take this medication, change these batteries, backup that machine, sync these folders/machines, make a regular phone call, check that radio, renew that contract, renew a certification, update an airplane’s GPS navigation database, etc., that I put it all in a database, and I put the parameters in a config file. E.g., do this daily, if done <= 24 hours ago, green, 24–36 hours yellow, beyond 36 hours, red. Every task item has different thresholds in terms of hours, days, weeks, months, … I try to keep the “task board” all-green, but if things slip into a yellow state, I know I need to do something soon, and if it slips into a red state, I have to do it immediately. All this stuff normally goes into a Calendar type application, and I do that too, but this dashboard approach goes beyond that and shows me the state of all the important things I’ve forgotten (or remembered) to do. Some of these things are to solve “sneaky” problems. The local water bill comes quarterly (Jan Apr Jul Oct) and is due or else. I’ve gotten shutoff notices and late charges in past years when that was the first notice I got. I marched down to the office and complained I never got the bill. (“Oh we did send it to you!” “Well I never got it!“) That was the origin of this particular app. I wanted to make sure I never had to go down to the water office to have those people berate me unfairly. So now it’s all tracked in my database, I know to check their website for the amount due and send it to them well ahead of their deadline. I did that this month, and I’m glad I did, because the July bill never came in the mail, but I was prepared, I beat them at their own game.
It’s great to have reminders for the periodical tasks. In Denmark most companies use a service where the consumer, can pay periodical bills automatically. It works surprisingly well.
Still need manually to report how much “heating” have been used (I have central heating).
The “task board” sounds like a great idea.
the magic incantation is raco pkg migrate 7.7
, I think
that will move everything you had installed in 7.7 into your current version
Thank you @notjack!
I probably got the idea from work or from Civil Air Patrol. In CAP we have a status board showing the aircraft in the Wing (US state), where they are, whether they are FMC / PMC / NMC (Full/Partially/Not) Mission Capable, how close they are to 100hr inspections, etc. At work (AccuWeather) we have a zillion status boards for all the services we run, when certain things go red the NOC (network operations center) calls me or one of my colleagues to fix problems immediately, so we like things to stay green (not broken) so we can sleep at night. There are also county/region alert areas for each country, e.g., it’s hot in Denmark as you probably already know, so this is the MeteoAlarm map for Denmark, and we have to monitor that the alerts we send to mobile devices precisely match the info coming from each country’s weather services. http://meteoalarm.eu/en_UK/0/0/DK-Denmark.html
Yeah - we have a heat wave at the moment. Just in time for schools to start…