what predicate is true for syntax objects with lexical context, and false for those without?

72 views
Skip to first unread message

Matthew Butterick

unread,
Dec 1, 2016, 6:23:29 PM12/1/16
to Racket Developers
`syntax?` is #t no matter what.

`syntax-source-module` is close, but according to the docs, it can still be #f even if the syntax object has a lexical context.

I considered this:

(define (no-lexical-context? stx)
  (equal? stx (strip-context stx)))

But `equal?` doesn't work here. (Side question: does `equal?` ever return #t for syntax objects, other than the trivial case of `(equal? stx stx)`?)


Matthew Flatt

unread,
Dec 1, 2016, 8:33:20 PM12/1/16
to Matthew Butterick, Racket Developers
At Thu, 1 Dec 2016 15:23:29 -0800 (PST), Matthew Butterick wrote:
> `syntax?` is #t no matter what.
>
> `syntax-source-module` is close, but according to the docs, it can still be
> #f even if the syntax object has a lexical context.
>
> I considered this:
>
> (define (no-lexical-context? stx)
> (equal? stx (strip-context stx)))
>
> But `equal?` doesn't work here.

Assuming that "without lexical context" means "no scopes"[*], I don't
think there's a simpler way right now than walking through the syntax
object and making sure that each scope set is empty. And maybe there's
no easier way to check for an empty scope set than using
`bound-identifier=?` on an identifier with a scope set to test and an
identifier that definitely has an empty scope set.

[*] I see that the Guide's section 17.3.2 says "no lexical context."
Maybe that would be better as "an empty lexical context" or even
"an empty set of scopes".

> (Side question: does `equal?` ever return
> #t for syntax objects, other than the trivial case of `(equal? stx stx)`?)

No, `equal?` is `eq?` for syntax objects.

Matthew Butterick

unread,
Dec 2, 2016, 1:32:23 PM12/2/16
to Matthew Flatt, Racket Developers
On Dec 1, 2016, 5:33 PM -0800, Matthew Flatt <mfl...@cs.utah.edu>, wrote:

Assuming that "without lexical context" means "no scopes"[*], I don't
think there's a simpler way right now than walking through the syntax
object and making sure that each scope set is empty. And maybe there's
no easier way to check for an empty scope set than using
`bound-identifier=?` on an identifier with a scope set to test and an
identifier that definitely has an empty scope set.

Specifically, what I was trying to do was write a contract for the return value of `read-syntax` that's narrower than just `syntax?` (which is insufficient, since `read-syntax` has to return a stx object without bindings). 

One can make such a stx object with `(datum->syntax #f ···)` or `(strip-context ···)`. So the mystery predicate here would return #t for stx objects made either of those ways.

PS is the name of `strip-context` obsolete? Should it be `strip-scope-sets`?

Alex Knauth

unread,
Dec 2, 2016, 2:21:21 PM12/2/16
to Matthew Butterick, Matthew Flatt, Racket Developers
Sent from my iPhone

>> On Dec 2, 2016, at 1:26 PM, Matthew Butterick <m...@mbtype.com> wrote:
>>
>> On Dec 1, 2016, 5:33 PM -0800, Matthew Flatt <mfl...@cs.utah.edu>, wrote:
>>
>> Assuming that "without lexical context" means "no scopes"[*], I don't
>> think there's a simpler way right now than walking through the syntax
>> object and making sure that each scope set is empty. And maybe there's
>> no easier way to check for an empty scope set than using
>> `bound-identifier=?` on an identifier with a scope set to test and an
>> identifier that definitely has an empty scope set.
>
> Specifically, what I was trying to do was write a contract for the return value of `read-syntax` that's narrower than just `syntax?` (which is insufficient, since `read-syntax` has to return a stx object without bindings).

Does that mean it will yell at you when used with langs like afl, curly-fn, and sugar/debug?

Or are you using this because you'll end up calling strip-context anyway?

> One can make such a stx object with `(datum->syntax #f ···)` or `(strip-context ···)`. So the mystery predicate here would return #t for stx objects made either of those ways.
>
> PS is the name of `strip-context` obsolete? Should it be `strip-scope-sets`?

No, the lexical information is still more than just the scope-sets, I believe it also includes other information about bindings. So strip-context is still relevant.

(See also functions like identifier-prune-lexical-context, which if I understand correctly leaves scopes alone and prunes other information)

Matthew Butterick

unread,
Dec 2, 2016, 2:38:32 PM12/2/16
to Alex Knauth, Racket Developers

On Dec 2, 2016, 11:21 AM -0800, Alex Knauth <alex...@knauth.org>, wrote:

Does that mean it will yell at you when used with langs like afl, curly-fn, and sugar/debug?

Or are you using this because you'll end up calling strip-context anyway?

You wouldn't use it with metalanguages. But according to the docs, "a `read-syntax` function [for a standalone #lang] should return a syntax object with no lexical context." [1] So I'm just wondering how one would express this idea in a predicate.



Alexis King

unread,
Dec 2, 2016, 3:01:22 PM12/2/16
to Alex Knauth, Matthew Butterick, Matthew Flatt, Racket Developers
> On Dec 2, 2016, at 11:21 AM, Alex Knauth <alex...@knauth.org> wrote:
>
> Does that mean it will yell at you when used with langs like afl, curly-fn, and sugar/debug?
>
> Or are you using this because you'll end up calling strip-context anyway?

This is a bit off-topic, and I don’t want to hijack this thread,
but I think those packages (my curly-fn package included) are a bit
of an abomination, and I wouldn’t spend too much time hand-wringing
about how to support them. I’d like to get rid of that if at all
possible, but it’s not currently clear how to do that. I have an
open PR, racket/racket#1458[1], that attempts to solve it, and
curly-fn is set up to support that. I have no idea if that’s a good
solution, though.

> No, the lexical information is still more than just the scope-sets, I believe it also includes other information about bindings. So strip-context is still relevant.
>
> (See also functions like identifier-prune-lexical-context, which if I understand correctly leaves scopes alone and prunes other information)

Do you have more information about this? I don’t think I ever looked
at identifier-prune-lexical-context in much detail, and it’s really
confusing to me. What exactly does that do and when would I need
it?

[1]: https://github.com/racket/racket/pull/1458

Matthew Flatt

unread,
Dec 3, 2016, 8:56:23 PM12/3/16
to Matthew Butterick, Racket Developers
Just to make sure we're on the same page, this is the function that I
had in mind:

(define (empty-scopes-everywhere? e)
(cond
[(syntax? e)
(and (bound-identifier=? (datum->syntax #f 'x)
(datum->syntax e 'x))
(empty-scopes-everywhere? (syntax-e e)))]
[(pair? e) (and (empty-scopes-everywhere? (car e))
(empty-scopes-everywhere? (cdr e)))]
[(vector? e) (for/and ([elem (in-vector e)])
(empty-scopes-everywhere? elem))]
[(box? e) (empty-scopes-everywhere? (unbox e))]
[(prefab-struct-key e)
(empty-scopes-everywhere? (struct->vector e))]
[else #t]))


> PS is the name of `strip-context` obsolete? Should it be `strip-scope-sets`?

Maybe so. In retrospect, I like how "lexical context" is less connected
to an implementation, but the the different layers of terminology don't
line up precisely.

Matthew Butterick

unread,
Dec 4, 2016, 3:05:14 PM12/4/16
to Matthew Flatt, Racket Developers
Just to make sure we're on the same page, this is the function that I
had in mind:

(define (empty-scopes-everywhere? e)

That works. Thank you.


PS is the name of `strip-context` obsolete? Should it be `strip-scope-sets`?

Maybe so. In retrospect, I like how "lexical context" is less connected
to an implementation, but the the different layers of terminology don't
line up precisely.

Racket is introduced as a "lexically scoped language" [1]. And it already uses "context" for a separate idea within the macro expander [2]. So I agree that "scopes" is the better term (because it embraces both a conceptual and implementational idea). Moreover, at this point, the use of the term "lexical context" to mean "the scopes attached to a certain syntax object" seems to cloud the issue.



Dupéron Georges

unread,
Jan 21, 2017, 1:45:18 PM1/21/17
to Racket Developers, m...@mbtype.com
Le dimanche 4 décembre 2016 02:56:23 UTC+1, mflatt a écrit :
(define (empty-scopes-everywhere? e)
  (cond
   [(syntax? e)
    (and (bound-identifier=? (datum->syntax #f 'x)
                             (datum->syntax e 'x))
         (empty-scopes-everywhere? (syntax-e e)))]
   [(pair? e) (and (empty-scopes-everywhere? (car e))
                   (empty-scopes-everywhere? (cdr e)))]
   [(vector? e) (for/and ([elem (in-vector e)])
                  (empty-scopes-everywhere? elem))]
   [(box? e) (empty-scopes-everywhere? (unbox e))]
   [(prefab-struct-key e)
    (empty-scopes-everywhere? (struct->vector e))]
   [else #t]))

This probably should also include a case for hash:

[(hash? e) (empty-scopes-everywhere (hash->list e))]
Reply all
Reply to author
Forward
0 new messages