@comer has joined the channel
What’s the right way for a language to generate code such that the bindings it references for core constructs are in a separate context from the bindings that user code can introduce? For example, in a custom language I have the following code to compile anonymous functions:
[(keyword 'fun)
(skip l 'lparen)
(with-syntax ([(arg ...) (parse-ident-list l)]
[(e ...) (parse-body l)])
(syntax/loc (token->syntax t)
(lambda (arg ...)
e ...)))]
so code in my language like
let f = fun(x) x end
compiles to
(define f (lambda (x) x))
which works great until the user writes something like
let lambda = 1
let f = fun(x) x end
at which point the lambda
in the compiled code will refer to the binding that the user introduced, which isn’t what I want. I’ve looked through the custom languages under racket/*
on GitHub, but I haven’t seen any that solve this particular problem (or I might have missed it if they did). I noticed datalog
does something with make-syntax-introducer
, but I’m not sure how I can create an introducer that is able to reference the module language of the resulting module.
I think you accidentally gave the identifiers a context in the reader. As an experiment, I tried this:
Running it gives 42.
When the macro is applied the identifiers in the input are marked (conceptually at least) by a syntax-introducer - giving the user supplied lambda and the lambda in the macro body different contexts.
Depending on the details, you might need to do this manually in the reader - but it depends on how your setup is.
My reader calls strip-context
on the generated syntax. My (probably flawed) understanding is that the effect of that is that the expander gets a context-less syntax and then introduces all the appropriate scopes itself, which seems to be what is happening. i.e. the program
let lambda = 1
let f = fun(x) x end
expands in the same way that the racket program
(define lambda 1)
(define f (lambda (x) 1))
does, which isn’t what I want. The bindings created in the generated program should have a different scope from the bindings referenced by language builtins or keywords.
What I’m trying to avoid is a user of this language creating a binding called “lambda” at the toplevel and then being confused about why fun
no longer works.
You can use a syntax introducer to give the implemented language a new scope:
(define introduce (make-syntax-introducer 'my-lang-scope))
Then use (introduce (strip-context ...))
in the reader.
(I think)
It seems like if I do that then
[(keyword 'fun)
(skip l 'lparen)
(with-syntax ([(arg ...) (parse-ident-list l)]
[(e ...) (parse-body l)])
(syntax/loc (token->syntax t)
(lambda (arg ...)
e ...)))]
the lambda
here will be placed in the same scope set up by introduce
, in which case I think I’d be back to square 1. If I do
#`(#,(datum->syntax #f 'lambda) (arg ...) e ...)
instead, then it’d reference the lambda
in my reader’s scope (assuming I also drop the strip-context
), which might be OK, but still doesn’t feel quite right. The language also has syntax for subscripting collection values
[(lsqbrace)
(skip l 'lsqbrace)
(define sub-expr (parse-expression l))
(skip l 'rsqbrace)
(loop (with-syntax ([e expr]
[arg-e sub-expr])
(syntax/loc expr
(subscript e arg-e))))]
so if I changed this in the same way then subscript
would have to be referenced from the reader’s scope, which doesn’t seem right.
(whereas now subscript
is provided by the module language)
which also doesn’t seem quite right, because subscript
should probably not be accessible by user code; so maybe it makes sense after all to have “builtins” be referenced through the reader’s scope
it seems like that might be roughly what algol60 is doing here:
https://github.com/racket/algol60/blob/master/compile.rkt#L398
though I don’t fully grok that code yet
Last “what if” :slightly_smiling_face:
What if you export introduce from the reader, and then do this: [(keyword 'fun)
(skip l 'lparen)
(with-syntax ([(arg ...) (parse-ident-list l)]
[(e ...) (parse-body l)])
(introduce
(syntax/loc (token->syntax t)
(lambda (arg ...)
e ...))))]
I think if I did that, strip-context
would just remove it. I’d probably have to replace strip-context
with a syntax delta introducer so I can surgically strip just the context of my compiler. I’ll give that a try
OK, wrapping the module in an introducer and then using it to flip the builtins seems to get rid of the original problem. Now the builtins get referenced through the compiler’s scope, which doesn’t seem that bad and has the advantage that they don’t have to be exposed in the resulting module. Thanks, @soegaard2!
Great.
@pavpanchekha also, the original scheme workshop paper is still a pretty good ffi reference
@pavpanchekha the Racket nlopt wrapper library has some examples of passing racket functions as C callbacks. However, one observation: it’s better to pass closures to C than to use the typical C clientData kind of interface. Closures “just work”. https://github.com/jkominek/nlopt
@arunsk.tec has joined the channel
@chas has joined the channel
@johnsmithaaa100 has joined the channel
@lcsfs11 has joined the channel
hello, I’m Lucas, new here. I was wondering, if A is a matrix defined as a list of lists (I’m doing some throwaway code for some abstract algebra homework computations), then (define (transpose A) (apply map list A)) does the right thing. If apply wasn’t intelligent enough to group map and list, would there be a similarly straightforward way of defining this function?
I’m not sure what you meant by “If apply wasn’t intelligent enough to group map and list”
If you use Racket, apply
would work the way you expect it to work.
I meant, in the hypothetical case that apply
could only be called with a function and the arguments, like (apply f lst)
Then (apply map (cons list A))
?
(apply f a … xs)
is really just a shorthand of (apply f (list* a ... xs))
which is equivalent to (f a ... x ...)
where (list x ...)
= xs
.
ah, now I get how apply
works
my question was whether I could craft a function f
, using map
and list
, in such a way that (apply f '((1 2 3) (4 5 6))
would be '((1 4) (2 5) (3 6))
. It was a puzzle-like question, though, out of curiosity.
@wlbberman Does this help?
Do you really mean (apply f '((1 2 3) (4 5 6))
? Or you actually want (f '((1 2 3) (4 5 6))
?
For the latter, that f
would be called transpose
, and it’s a standard exercise on list manipulation :slightly_smiling_face:
I meant the former, I figured (lambda (A) (apply map list A))
does the latter, and you helped me understand why :+1:
I wrote a require transformer that searches parent directories for an rcfile.
(define-syntax nearest-rc
(make-require-transformer
(λ (stx)
(define dir (syntax-source-directory stx))
(unless (path? dir)
(raise-syntax-error 'nearest-u/a
"Cannot find search directory. Are you using this macro in a file on disk?"))
(define p (find-config/with-cache dir))
(unless (path? p)
(error 'nearest-u/a
"Cannot find config file when searching from ~a" dir))
(with-syntax ([path (path->string p)])
(expand-import #'path)))))
The macro stepper shows that (require (nearest-rc))
expands to something like this. (#%require (just-meta 0 (rename (file "/home/sage/Code/unlike-assets/unlike-assets/unlike-assets/try.rkt") data data))
(only (file "/home/sage/Code/unlike-assets/unlike-assets/unlike-assets/try.rkt")))
data
is indeed provided from the try.rkt
module that you see in the expansion, but the REPL complains that data
is not bound. What do I need to do to the transformer to fix this?
Ah, I got it.
The syntax object needed some lexical context from stx
. The solution seemed to be changing the expand-import
as follows. (expand-import (datum->syntax stx `(file ,(path->string p))))
@ashleynykiel has joined the channel
Sometimes I’m like, “Emacs, go home, you’re drunk because you’ve seen too much over too many decades of computing history”. :simple_smile: https://github.com/greghendershott/racket-mode/issues/450
OTOH maybe this is what Emacs is telling me.
Possibly, thank you I’ll take a look
how do i evaluate a .rkt file through CLI?
racket -f <filename> loads it in
trying to debug this error: make-directory: cannot make directory
path: C:\Program Files\Racket\share\pkgs\errortrace-lib\errortrace\compiled\drracket\
system error: Permission denied; errno=13
Trying to use pollens select function https://docs.racket-lang.org/pollen/Core.html?q=select#%28def._%28%28lib._pollen%2Fcore..rkt%29._select%29%29 to read a file and grab the date. I tried to call it in multiple ways , (select 'time (build-path (current-directory) "chapter1" "test.html"))