[racket] How to tell if identifier is bound in racket/load?

78 views
Skip to first unread message

Nick Sivo

unread,
Jul 16, 2012, 3:35:48 AM7/16/12
to us...@racket-lang.org
Hi,

I've been using the following awesome snippet to detect when
identifiers are already bound:

; From Danny Yoo's arctangent
; https://github.com/dyoo/arctangent/blob/master/language.rkt
; Returns true if stx is an identifier that is lexically bound.
(define-for-syntax (lexically-bound? stx)
(let ([expanded (local-expand stx (syntax-local-context) #f)])
(cond
[(and (identifier? expanded)
(eq? #f (identifier-binding expanded)))
#f]
[else
#t])))

Then I created a helper so I could call it at runtime:

(define-syntax (arc-bound stx)
(syntax-parse
stx
[(_ var:id)
(if (lexically-bound? #'var) #'#t #'#f)]))

That still worked great. Then I tried using racket/load:

Welcome to DrRacket, version 5.2.1 [3m].
Language: racket/load; memory limit: 128 MB.
> (arc-bound a)
#f
> (define a 5)
> (arc-bound a)
#f
>

:( I've read through the docs on namespaces and the racket/load
language source in an attempt to fully understand why racket/load is
different, but I'm missing something. I've noticed that I can see 'a
in (namespace-mapped-symbols), and have tried using that, but ran into
(I think) phase issues. If there's a quick solution to this that I've
missed, that's brilliant.

Otherwise, my motivation to start making use of racket/load (really
arc/load in my case), is to get access to global variables. If
ark.rkt declares something at the module level, I need to be able to
see it - the same copy of it - from other modules that require
ark.rkt. What I'm trying now is to have ark.rkt be a real module
written in arc, and then require it and load the rest of the arc files
using the arc/load language so they all share the same module registry
and the same ark.rkt. If there's some other, better way to do this,
pointers will be quite warmly received.

I'm also becoming aware that at least for now I'm probably one of
those users who knows just enough to be dangerous, but not enough to
save myself. I welcome challenges to my approach, corrections to my
descriptions, and inquiries for more information. It's possible I'm
asking for details about the completely wrong approach, when really
something else would be more appropriate.

-Nick
____________________
Racket Users list:
http://lists.racket-lang.org/users

Ryan Culpepper

unread,
Jul 16, 2012, 1:43:13 PM7/16/12
to Nick Sivo, us...@racket-lang.org
It works in DrRacket using #lang racket because the interactions occur
in a module namespace, but with #lang racket/load all interactions
happen in an ordinary namespace. That accounts for the differences in
the results of identifier-binding.

> Otherwise, my motivation to start making use of racket/load (really
> arc/load in my case), is to get access to global variables. If
> ark.rkt declares something at the module level, I need to be able to
> see it - the same copy of it - from other modules that require
> ark.rkt. What I'm trying now is to have ark.rkt be a real module
> written in arc, and then require it and load the rest of the arc files
> using the arc/load language so they all share the same module registry
> and the same ark.rkt. If there's some other, better way to do this,
> pointers will be quite warmly received.

I can't tell how the behavior you described differs from just providing
the variable from ark.rkt and requiring ark.rkt normally. Can you elaborate?

Ryan

Nick Sivo

unread,
Jul 16, 2012, 8:23:07 PM7/16/12
to Ryan Culpepper, us...@racket-lang.org
> I can't tell how the behavior you described differs from just providing the
> variable from ark.rkt and requiring ark.rkt normally. Can you elaborate?

Wow. I'm not sure what misconceptions I was working under, but that
works completely fine. And it looks like I can use something like
defm from http://permalink.gmane.org/gmane.comp.lang.racket.user/13119
to hide the getters and setters.

Thanks!

That said, I'd still like to be able to enable the equivalent of the
following in arc:

#lang racket/load

(define (test1)
(displayln "a"))

(define (test2)
(test1))

(test2)

(define (test1)
(displayln "b"))

(test2)

Welcome to DrRacket, version 5.2.1 [3m].
Language: racket/load [custom]; memory limit: 128 MB.
a
b

My current macro to handle top level assignment doesn't work in racket/load:

#lang racket/load

(require (for-syntax syntax/parse))

; From Danny Yoo's arctangent
; https://github.com/dyoo/arctangent/blob/master/language.rkt
; Returns true if stx is an identifier that is lexically bound.
(define-for-syntax (lexically-bound? stx)
(let ([expanded (local-expand stx (syntax-local-context) #f)])
(cond
[(and (identifier? expanded)
(eq? #f (identifier-binding expanded)))
#f]
[else #t])))

(define-syntax (assign1 stx)
(define-splicing-syntax-class binding-pair
#:description "binding pair"
(pattern (~seq var:id rhs:expr)))
(syntax-parse
stx
[(_ p:binding-pair)
(if (lexically-bound? #'p.var)
#'(begin (set! p.var p.rhs)
p.var)
#'(begin (define p.var #f)
(assign1 p.var p.rhs)))]))

(assign1 a 5)

When run, it never completes because lexically-bound? always returns
false and recursion never terminates.

If instead the assignment is in the body of a function, or in a
module, it all works fine:

(define (test)
(assign a 5)
a)

Do these examples make my intent more clear? I'm not sure how to
describe what I want correctly.

-Nick

Ryan Culpepper

unread,
Jul 17, 2012, 3:11:35 PM7/17/12
to Nick Sivo, us...@racket-lang.org
I'm not sure how robust this solution is, but you could change the
evaluation namespace to an empty module namespace. (Instead of an empty
ordinary namespace, which is what #lang racket/load creates.)

In your example above, insert the following three lines right after the
#lang line:

(require racket/enter)
(module scratch racket)
(enter! 'scratch)

Then you could create your own language (arc/load?) that adds those
lines (or something similar) automatically.
Reply all
Reply to author
Forward
0 new messages