29
78
Fork
You've already forked r7rs
2

Quasiquotation of homogeneous vectors #312

Open
opened 2026年03月31日 17:47:18 +02:00 by johnwcowan · 24 comments

Should we allow quasiquotation of homogeneous vectors from SRFIs 4 and 160? They would look like #f64(1.1 ,x), where x must be bound to an inexact number.

Should we allow quasiquotation of homogeneous vectors from SRFIs 4 and 160? They would look like `#f64(1.1 ,x)`, where x must be bound to an inexact number.
Owner
Copy link

No.

What would the syntax object representation be? To support this it must be possible for an @vector to store and retrieve values of at least any datum type other than @, which would defeat their objective

No. What would the syntax object representation be? To support this it must be possible for an `@vector` to store and retrieve values of at least any datum type other than `@`, which would defeat their objective
Author
Owner
Copy link

@dpk wrote in #312 (comment):

To support this it must be possible for an @vector to store and retrieve values of at least any datum type

Can you explain why?

@dpk wrote in https://codeberg.org/scheme/r7rs/issues/312#issuecomment-12482331: > To support this it must be possible for an `@vector` to store and retrieve values of at least any datum type Can you explain why?
Member
Copy link

An @vector syntax object need not be a numeric @vector. It is perfectly possible (and sensible) that any syntax datum can be put into a @vector syntax object. Only when that syntax object is read as an expression by the evaluator, it would be an error if its stripped version contained a non-number.

So what @johnwcowan wants (and users probably expect to be supported) can work.

An `@vector` syntax object need not be a numeric `@vector`. It is perfectly possible (and sensible) that any syntax datum can be put into a `@vector` syntax object. Only when that syntax object is read as an expression by the evaluator, it would be an error if its stripped version contained a non-number. So what @johnwcowan wants (and users probably expect to be supported) can work.

@johnwcowan wrote in #312 (comment):

@dpk wrote in #312 (评论):

To support this it must be possible for an @vector to store and retrieve values of at least any datum type

Can you explain why?

Consider a vector with quasiquote `#(1 2 3 ,(+ 1 3)), in many implementations, it would be read as (quasiquote #(1 2 3 (unquote (+ 1 3)))).

The part #(1 2 3 (unquote (+ 1 3))), if we treat it independently, is a valid Scheme object and also a valid datum.

However, if we write things like `#vu8(1 2 3 ,(+ 1 3)), #vu8(1 2 3 (unquote (+ 1 3))) is not a valid object. Every sane Scheme implementation will reject it at read time.

Copyright: IIRC, this example was given by @dpk in IRC.

@johnwcowan wrote in https://codeberg.org/scheme/r7rs/issues/312#issuecomment-12483477: > @dpk wrote in #312 (评论): > > > To support this it must be possible for an `@vector` to store and retrieve values of at least any datum type > > Can you explain why? Consider a vector with quasiquote `` `#(1 2 3 ,(+ 1 3)) ``, in many implementations, it would be read as `(quasiquote #(1 2 3 (unquote (+ 1 3))))`. The part `#(1 2 3 (unquote (+ 1 3)))`, if we treat it independently, is a valid Scheme object and also a valid datum. However, if we write things like `` `#vu8(1 2 3 ,(+ 1 3)) ``, `#vu8(1 2 3 (unquote (+ 1 3)))` is not a valid object. Every sane Scheme implementation will reject it at read time. Copyright: IIRC, this example was given by @dpk in IRC.
Member
Copy link

@readevalprintloop wrote in #312 (comment):

@johnwcowan wrote in #312 (Kommentar):

@dpk wrote in #312 (评论):

To support this it must be possible for an @vector to store and retrieve values of at least any datum type

Can you explain why?

Consider a vector with quasiquote `#(1 2 3 ,(+ 1 3)), in many implementations, it would be read as (quasiquote #(1 2 3 (unquote (+ 1 3)))).

The part #(1 2 3 (unquote (+ 1 3))), if we treat it independently, is a valid Scheme object and also a valid datum.

However, if we write things like `#vu8(1 2 3 ,(+ 1 3)), #vu8(1 2 3 (unquote (+ 1 3))) is not a valid object. Every sane Scheme implementation will reject it at read time.

No, not necessarily. See my previous post. One lesson R6RS told us is that the hygienic macro model of Scheme forces us to distinguish between datum objects and syntax objects.

There is no datum value represented by the example you gave. But it makes sense to have a syntax object type to represent it.

@readevalprintloop wrote in https://codeberg.org/scheme/r7rs/issues/312#issuecomment-12595398: > @johnwcowan wrote in #312 (Kommentar): > > > @dpk wrote in #312 (评论): > > > To support this it must be possible for an `@vector` to store and retrieve values of at least any datum type > > > > > > Can you explain why? > > Consider a vector with quasiquote `` `#(1 2 3 ,(+ 1 3)) ``, in many implementations, it would be read as `(quasiquote #(1 2 3 (unquote (+ 1 3))))`. > > The part `#(1 2 3 (unquote (+ 1 3)))`, if we treat it independently, is a valid Scheme object and also a valid datum. > > However, if we write things like `` `#vu8(1 2 3 ,(+ 1 3)) ``, `#vu8(1 2 3 (unquote (+ 1 3)))` is not a valid object. Every sane Scheme implementation will reject it at read time. No, not necessarily. See my previous post. One lesson R6RS told us is that the hygienic macro model of Scheme forces us to distinguish between datum objects and syntax objects. There is no datum value represented by the example you gave. But it makes sense to have a syntax object type to represent it.

@mnw wrote in #312 (comment):

@readevalprintloop wrote in #312 (评论):

@johnwcowan wrote in #312 (Kommentar):

@dpk wrote in #312 (评论):

To support this it must be possible for an @vector to store and retrieve values of at least any datum type

Can you explain why?

Consider a vector with quasiquote `#(1 2 3 ,(+ 1 3)), in many implementations, it would be read as (quasiquote #(1 2 3 (unquote (+ 1 3)))).
The part #(1 2 3 (unquote (+ 1 3))), if we treat it independently, is a valid Scheme object and also a valid datum.
However, if we write things like `#vu8(1 2 3 ,(+ 1 3)), #vu8(1 2 3 (unquote (+ 1 3))) is not a valid object. Every sane Scheme implementation will reject it at read time.

No, not necessarily. See my previous post. One lesson R6RS told us is that the hygienic macro model of Scheme forces us to distinguish between datum objects and syntax objects.

There is no datum value represented by the example you gave. But it makes sense to have a syntax object type to represent it.

But your solution seems to imply that there's another procedure read-syntax, which is different from regular read and can be used to read an homogeneous vector with unquote or unquote-splicing inside (e.g. `#u8(1 2 ,(+ 1 2))) as a syntax object.

In my view, this could lead following issues:

  • The "quasi homogeneous vector" is not a datum and can't be read by procedure read. We may even unable to create such syntax objects at runtime.
  • Thus, we can't create a datum represents a program using this construct and pass it to eval. Though in the program that uses eval does not, in principle, require such construct. But it does harm the meta-circularity of the language.
  • They're not syntax objects that directly derived from datum, so what would happen if we call syntax->datum on it?

R6RS introduce a conceptual type "syntax object". However, these syntax objects can always derived from data.

@mnw wrote in https://codeberg.org/scheme/r7rs/issues/312#issuecomment-12595521: > @readevalprintloop wrote in #312 (评论): > > > @johnwcowan wrote in #312 (Kommentar): > > > @dpk wrote in #312 (评论): > > > > To support this it must be possible for an `@vector` to store and retrieve values of at least any datum type > > > > > > > > > Can you explain why? > > > > > > Consider a vector with quasiquote `` `#(1 2 3 ,(+ 1 3)) ``, in many implementations, it would be read as `(quasiquote #(1 2 3 (unquote (+ 1 3))))`. > > The part `#(1 2 3 (unquote (+ 1 3)))`, if we treat it independently, is a valid Scheme object and also a valid datum. > > However, if we write things like `` `#vu8(1 2 3 ,(+ 1 3)) ``, `#vu8(1 2 3 (unquote (+ 1 3)))` is not a valid object. Every sane Scheme implementation will reject it at read time. > > No, not necessarily. See my previous post. One lesson R6RS told us is that the hygienic macro model of Scheme forces us to distinguish between datum objects and syntax objects. > > There is no datum value represented by the example you gave. But it makes sense to have a syntax object type to represent it. But your solution seems to imply that there's another procedure `read-syntax`, which is different from regular `read` and can be used to read an homogeneous vector with `unquote` or `unquote-splicing` inside (e.g. `` `#u8(1 2 ,(+ 1 2)) ``) as a syntax object. In my view, this could lead following issues: - The "quasi homogeneous vector" is not a datum and can't be read by procedure `read`. We may even unable to create such syntax objects at runtime. - Thus, we can't create a datum represents a program using this construct and pass it to `eval`. Though in the program that uses `eval` does not, in principle, require such construct. But it does harm the meta-circularity of the language. - They're not syntax objects that directly derived from datum, so what would happen if we call `syntax->datum` on it? R6RS introduce a conceptual type "syntax object". However, these syntax objects can always derived from data.
Member
Copy link
  1. Any good Scheme system has a procedure that may be called read-syntax. Ordinary read does not collect line number information, for example, and when evaluating/compiling a Scheme program, users expect source location information. It makes sense to standardise an interface to such a procedure.
  2. Yes, a quasi-homogeneous vector won't be a datum, the same way as an identifier is not a datum.
  3. The syntax form can be used to create syntax at runtime, including quasi-homogeneous vectors.
  4. The eval interface is partly from an ancient time when Scheme had no hygienic vectors and when datums represented Scheme expressions. eval-syntax should accompany read-syntax.
  5. syntax->datum would raise a violation. But that's okay. It is already a lossy conversion. In some sense, identifier->symbol is all that is really needed. Every other use of syntax->datum can be arguably solved differently in a more elegant way.
1. Any good Scheme system has a procedure that may be called `read-syntax`. Ordinary `read` does not collect line number information, for example, and when evaluating/compiling a Scheme program, users expect source location information. It makes sense to standardise an interface to such a procedure. 2. Yes, a quasi-homogeneous vector won't be a datum, the same way as an identifier is not a datum. 3. The `syntax` form can be used to create syntax at runtime, including quasi-homogeneous vectors. 4. The `eval` interface is partly from an ancient time when Scheme had no hygienic vectors and when datums represented Scheme expressions. `eval-syntax` should accompany `read-syntax`. 5. `syntax->datum` would raise a violation. But that's okay. It is already a lossy conversion. In some sense, `identifier->symbol` is all that is really needed. Every other use of `syntax->datum` can be arguably solved differently in a more elegant way.
Author
Owner
Copy link

@mnw wrote in #312 (comment):

  1. Any good Scheme system has a procedure that may be called read-syntax. Ordinary read does not collect line number information, for example, and when evaluating/compiling a Scheme program, users expect source location information.

Chez does and so does Racket, but I think they are the only ones.

@mnw wrote in https://codeberg.org/scheme/r7rs/issues/312#issuecomment-12618156: > 1. Any good Scheme system has a procedure that may be called `read-syntax`. Ordinary `read` does not collect line number information, for example, and when evaluating/compiling a Scheme program, users expect source location information. Chez does and so does Racket, but I think they are the only ones.
Owner
Copy link

syntax->datum is one thing, but what does unwrap-syntax (syntax-e) return? That is needed to implement syntax-case, and for syntax-case to be able to match within a numeric vector to implement quasiquote, which is the point here.

`syntax->datum` is one thing, but what does `unwrap-syntax` (`syntax-e`) return? That is needed to implement `syntax-case`, and for `syntax-case` to be able to match within a numeric vector to implement quasiquote, which is the point here.
Member
Copy link

@johnwcowan wrote in #312 (comment):

@mnw wrote in #312 (Kommentar):

  1. Any good Scheme system has a procedure that may be called read-syntax. Ordinary read does not collect line number information, for example, and when evaluating/compiling a Scheme program, users expect source location information.

Chez does and so does Racket, but I think they are the only ones.

Internally, most Schemes likely have such a thing. The difference is that Chez and Racket export it under non-standardised names. In any case, it is useful to have for anyone wanting to read Scheme lexical syntax and be able to report source locations, for example.

@johnwcowan wrote in https://codeberg.org/scheme/r7rs/issues/312#issuecomment-12622365: > @mnw wrote in #312 (Kommentar): > > > 1. Any good Scheme system has a procedure that may be called `read-syntax`. Ordinary `read` does not collect line number information, for example, and when evaluating/compiling a Scheme program, users expect source location information. > > Chez does and so does Racket, but I think they are the only ones. Internally, most Schemes likely have such a thing. The difference is that Chez and Racket export it under non-standardised names. In any case, it is useful to have for anyone wanting to read Scheme lexical syntax and be able to report source locations, for example.
Member
Copy link

@dpk wrote in #312 (comment):

syntax->datum is one thing, but what does unwrap-syntax (syntax-e) return? That is needed to implement syntax-case, and for syntax-case to be able to match within a numeric vector to implement quasiquote, which is the point here.

Indeed, syntax-case is the tool provided to destruct syntax objects. A syntax object is like an object of an algebraic data type, and syntax-case is the corresponding matcher/destructor.

syntax-case can be implemented in various ways. If you want to be able to implement it as library syntax with something like unwrap-syntax, you will have to add a type that unwrap-syntax returns as a container for vector-like types (such a container can consist of a tag-field together with element-fields). But this is not particularly important for the user of the language, only for writers of the standard library. Exposing something like unwrap-syntax became an idea during R7RS-large (and R6RS works very well without it). It has its value for academic discussions (and for the appendix to R4RS), but neither is it needed for nor should it dictate the surface language a user of R7RS sees.

@dpk wrote in https://codeberg.org/scheme/r7rs/issues/312#issuecomment-12622662: > `syntax->datum` is one thing, but what does `unwrap-syntax` (`syntax-e`) return? That is needed to implement `syntax-case`, and for `syntax-case` to be able to match within a numeric vector to implement quasiquote, which is the point here. Indeed, `syntax-case` is the tool provided to destruct syntax objects. A syntax object is like an object of an algebraic data type, and `syntax-case` is the corresponding matcher/destructor. `syntax-case` can be implemented in various ways. If you want to be able to implement it as library syntax with something like `unwrap-syntax`, you will have to add a type that `unwrap-syntax` returns as a container for vector-like types (such a container can consist of a tag-field together with element-fields). But this is not particularly important for the user of the language, only for writers of the standard library. Exposing something like `unwrap-syntax` became an idea during R7RS-large (and R6RS works very well without it). It has its value for academic discussions (and for the appendix to R4RS), but neither is it needed for nor should it dictate the surface language a user of R7RS sees.
Member
Copy link

I should make it clear that what I said applies to that when a new vector lexical syntax is added to the language, no fundamental reason speaks against quasi-quotation in source code, and users of the language can rightly expect that it should work.

However, I don't think that any such new lexical syntax should be added to the language. People always praise Scheme for having such a simple syntax, while, in fact, the actual lexical syntax has become much more complicated than the basic syntax of s-exprs. Indeed, there is already no fundamental reason why we need special syntax for vectors (or bytevectors starting with R6RS). String syntax is needed, but for a very particular reason: No one wants to write strings in source code as a list of characters.

I should make it clear that what I said applies to that when a new vector lexical syntax is added to the language, no fundamental reason speaks against quasi-quotation in source code, and users of the language can rightly expect that it should work. However, I don't think that any such new lexical syntax should be added to the language. People always praise Scheme for having such a simple syntax, while, in fact, the actual lexical syntax has become much more complicated than the basic syntax of s-exprs. Indeed, there is already no fundamental reason why we need special syntax for vectors (or bytevectors starting with R6RS). String syntax is needed, but for a very particular reason: No one wants to write strings in source code as a list of characters.
Member
Copy link

One of the benefits of an entity being having lexical syntax is that it is a datum and can be passed to and returned from syntax transformers (a syntax transformer can manipulate a string to implement string interpolation, for example).

Syntax transformers could be extended to return non-datums (or even to all types), but specifying that would be trickier. And you'd have a "syntax" object with no lexical syntax.

One of the benefits of an entity being having lexical syntax is that it is a datum and can be passed to and returned from syntax transformers (a syntax transformer can manipulate a string to implement string interpolation, for example). Syntax transformers could be extended to return non-datums (or even to all types), but specifying that would be trickier. And you'd have a "syntax" object with no lexical syntax.
Member
Copy link

@phm wrote in #312 (comment):

One of the benefits of an entity being having lexical syntax is that it is a datum and can be passed to and returned from syntax transformers (a syntax transformer can manipulate a string to implement string interpolation, for example).

Why should there be a problem encoding some object (like a multidimensional array) as a syntax object (that roughly corresponds to a syntax object for an s-expr) for communication between syntax transformers? They already have to do such an encoding/decoding for record types/hashtables/whatever.

@phm wrote in https://codeberg.org/scheme/r7rs/issues/312#issuecomment-12625170: > One of the benefits of an entity being having lexical syntax is that it is a datum and can be passed to and returned from syntax transformers (a syntax transformer can manipulate a string to implement string interpolation, for example). Why should there be a problem encoding some object (like a multidimensional array) as a syntax object (that roughly corresponds to a syntax object for an s-expr) for communication between syntax transformers? They already have to do such an encoding/decoding for record types/hashtables/whatever.
Member
Copy link

@mnw wrote in #312 (comment):

Why should there be a problem encoding some object (like a multidimensional array) as a syntax object (that roughly corresponds to a syntax object for an s-expr) for communication between syntax transformers? They already have to do such an encoding/decoding for record types/hashtables/whatever.

This argument can be used to advocate for getting rid of record types entirely. Why not just always operate on trees of cons cells?

Serializing a non-datum compound type into a macro transformer means you a) have to pay the cost to serialize/desrialize an object constantly to operate on it, or b) redefine all of your operations on the serialized representation. Do I want to define inheritable subtyping predicates on serialized records?

@mnw wrote in https://codeberg.org/scheme/r7rs/issues/312#issuecomment-12625446: > Why should there be a problem encoding some object (like a multidimensional array) as a syntax object (that roughly corresponds to a syntax object for an s-expr) for communication between syntax transformers? They already have to do such an encoding/decoding for record types/hashtables/whatever. This argument can be used to advocate for getting rid of record types entirely. Why not just always operate on trees of cons cells? Serializing a non-datum compound type into a macro transformer means you a) have to pay the cost to serialize/desrialize an object constantly to operate on it, or b) redefine all of your operations on the serialized representation. Do I want to define inheritable subtyping predicates on serialized records?
Member
Copy link

@phm wrote in #312 (comment):

@mnw wrote in #312 (Kommentar):

Why should there be a problem encoding some object (like a multidimensional array) as a syntax object (that roughly corresponds to a syntax object for an s-expr) for communication between syntax transformers? They already have to do such an encoding/decoding for record types/hashtables/whatever.

This argument can be used to advocate for getting rid of record types entirely. Why not just always operate on trees of cons cells?

No. Record types are currently not supported as syntax objects, nor do they have read/write invariance. Yet, they have great use in actual code that is executed at runtime. You wouldn't gain much by allowing record types in syntax objects (or being able to read them).

Serializing a non-datum compound type into a macro transformer means you a) have to pay the cost to serialize/desrialize an object constantly to operate on it, or b) redefine all of your operations on the serialized representation. Do I want to define inheritable subtyping predicates on serialized records?

At runtime, no cost is paid at all. Syntax transformer procedures that do lots of work by calling helper procedures, whatever, ... also do not have to serialise/deserialise. The only case when this has to be done is when two macro invocations by the expander have to interact with each other. This is not a bottleneck at all.

For AOT expansion of libraries, it is important that syntax objects can be serialised, so the output of a syntax transformer (as part of a macro invocation) must not be arbitrary data, anyway.

@phm wrote in https://codeberg.org/scheme/r7rs/issues/312#issuecomment-12625623: > @mnw wrote in #312 (Kommentar): > > > Why should there be a problem encoding some object (like a multidimensional array) as a syntax object (that roughly corresponds to a syntax object for an s-expr) for communication between syntax transformers? They already have to do such an encoding/decoding for record types/hashtables/whatever. > > This argument can be used to advocate for getting rid of record types entirely. Why not just always operate on trees of cons cells? No. Record types are currently not supported as syntax objects, nor do they have read/write invariance. Yet, they have great use in actual code that is executed at runtime. You wouldn't gain much by allowing record types in syntax objects (or being able to `read` them). > Serializing a non-datum compound type into a macro transformer means you a) have to pay the cost to serialize/desrialize an object constantly to operate on it, or b) redefine all of your operations on the serialized representation. Do I want to define inheritable subtyping predicates on serialized records? At runtime, no cost is paid at all. Syntax transformer procedures that do lots of work by calling helper procedures, whatever, ... also do not have to serialise/deserialise. The only case when this has to be done is when two macro invocations by the expander have to interact with each other. This is not a bottleneck at all. For AOT expansion of libraries, it is important that syntax objects can be serialised, so the output of a syntax transformer (as part of a macro invocation) must not be arbitrary data, anyway.
Member
Copy link
  1. In fact, the more I think about it, unwrap-syntax is the wrong abstraction when the goal is to find fundamental procedures/syntax for syntax objects. Syntax objects make up an algebraic data type, so the fundamental procedure/syntax for it is a matcher (if we want syntax) or a suitable fold (if we want procedures). unwrap-syntax is just a partial converter and not as fundamental. It is a different question whether the fundamental matcher should be as full-blown as syntax-case or whether it should be something basic.
  2. Regarding serialising objects: Utility procedures in the standard library that serialise/deserialise "most" objects into syntax would be nice to have for macro writers.
1. In fact, the more I think about it, `unwrap-syntax` is the wrong abstraction when the goal is to find fundamental procedures/syntax for syntax objects. Syntax objects make up an algebraic data type, so the fundamental procedure/syntax for it is a matcher (if we want syntax) or a suitable fold (if we want procedures). `unwrap-syntax` is just a partial converter and not as fundamental. It is a different question whether the fundamental matcher should be as full-blown as `syntax-case` or whether it should be something basic. 2. Regarding serialising objects: Utility procedures in the standard library that serialise/deserialise "most" objects into syntax would be nice to have for macro writers.
Member
Copy link

I had to come up with a practical example of @dpk & @readevalprintloop's point. I think it captures why this is a weird idea in practice. Here it is: If I read

`#u16(,(+ 1 0))

then I get a list (quasiquote <something>) where <something> is, I guess, an invalid u16-vector.* You could extract that weird u16-vector with cadr and pass it to things that expect u16-vectors, which is not so good.

How do you deal with this? Is read supposed to allow this syntax and "pass the buck" on error detection? Then how do you prevent people from creating heterogeneous homogeneous vector-things by this kind of read hack?

*: It's invalid, of course, because the first element is (unquote (+ 1 0)).

I had to come up with a practical example of @dpk & @readevalprintloop's point. I think it captures why this is a weird idea in practice. Here it is: If I `read` > `#u16(,(+ 1 0)) then I get a list `(quasiquote <something>)` where `<something>` is, I guess, an invalid u16-vector.* You could extract that weird u16-vector with `cadr` and pass it to things that expect u16-vectors, which is not so good. How do you deal with this? Is `read` supposed to allow this syntax and "pass the buck" on error detection? Then how do you prevent people from creating heterogeneous homogeneous vector-things by this kind of `read` hack? *: It's invalid, of course, because the first element is `(unquote (+ 1 0))`.
Member
Copy link

There is nothing weird about it. The correct theoretical model behind this is that a procedure like read-syntax is the fundamental procedure. Its task is to turn a textual representation into a Scheme program (or fragment thereof), and as we know, since the R4RS-macro appendix, Scheme programs are no longer datums but syntax objects. The procedure read is conceptually the composition of read-syntax and syntax->datum. Its main use is to read data files encoded in Scheme datum syntax, but when used to read Scheme source code, it only gives an approximation.

As the example given does not represent a datum value, syntax->datum will signal an error and read will propagate this error. And that's completely fine and shouldn't come unexpected. Already in existing standards, syntax objects on one hand and datum values on the other hand do not form isomorphic families (think of identifiers).

To give you another example: if you use (quote #u16(,(+ 1 0))) in your code, the implementation's reader (read-syntax) won't have any problem with this expression and the expander of quote will be given a syntax object representing the example you gave. It will be that expander that signals an error because it will conceptually call syntax->datum by definition of quote.

There is nothing weird about it. The correct theoretical model behind this is that a procedure like `read-syntax` is the fundamental procedure. Its task is to turn a textual representation into a Scheme program (or fragment thereof), and as we know, since the R4RS-macro appendix, Scheme programs are no longer datums but syntax objects. The procedure `read` is conceptually the composition of `read-syntax` and `syntax->datum`. Its main use is to read data files encoded in Scheme datum syntax, but when used to read Scheme source code, it only gives an approximation. As the example given does not represent a datum value, `syntax->datum` will signal an error and `read` will propagate this error. And that's completely fine and shouldn't come unexpected. Already in existing standards, syntax objects on one hand and datum values on the other hand do not form isomorphic families (think of identifiers). To give you another example: if you use `(quote #u16(,(+ 1 0)))` in your code, the implementation's reader (`read-syntax`) won't have any problem with this expression and the expander of `quote` will be given a syntax object representing the example you gave. It will be that expander that signals an error because it will conceptually call `syntax->datum` by definition of `quote`.

I saw @johnwcowan wondering how Common Lisp handling the unquote{,-splicing} in #S (struct) and other lexical syntax in IRC. I think it's better to discuss it here instead of IRC.

IIUC, Hyperspec doesn't require a backquote form must be interpreted as a S-exp representation. Quoting from Hyperspec

An implementation is free to interpret a backquoted form F1 as any form F2 that, when evaluated, will produce a result that is the same under equal as the result implied by the above definition, provided that the side-effect behavior of the substitute form F2 is also consistent with the description given above. The constructed copy of the template might or might not share list structure with the template itself. As an example, the above definition implies that

`((,a b) ,c ,@d)

will be interpreted as if it were

(append (list (append (list a) (list 'b) 'nil)) (list c) d 'nil)

but it could also be legitimately interpreted to mean any of the following:

(append (list (append (list a) (list 'b))) (list c) d)
(append (list (append (list a) '(b))) (list c) d)
(list* (cons a '(b)) c d)
(list* (cons a (list 'b)) c d)
(append (list (cons a '(b))) (list c) d)
(list* (cons a '(b)) c (copy-list d))

Therefore, CL implementations may freely extend their backquote to support interpolation in other lexical syntax, without worrying introducing malformed syntax object.

This stands in stark contrast to the wording in R6RS/R7RS-Small, which states:

R6RS 4.3.5

'‌‌
<datum>‌‌ ,<datum>‌‌ ,@<datum>‌‌ #'<datum>‌‌ #‌‌
#,‌‌
#,@‌‌

Each of these is an abbreviation:
‌' for (quote ),
<datum> for (quasiquote <datum>), ‌,<datum> for (unquote <datum>), ‌,@<datum> for (unquote-splicing <datum>), ‌#'<datum> for (syntax <datum>), ‌# for (quasisyntax ),
‌#, for (unsyntax ), and
‌#,@ for (unsyntax-splicing

R7RS 4.2.8

The two notations `〈qq template〉 and (quasiquote
〈qq template〉) are identical in all respects. ,〈expression〉
is identical to (unquote 〈expression〉), and ,@〈expression〉
is identical to (unquote-splicing 〈expression〉). The
write procedure may output either format.

I saw @johnwcowan wondering how Common Lisp handling the unquote{,-splicing} in `#S` (struct) and other lexical syntax in IRC. I think it's better to discuss it here instead of IRC. IIUC, [Hyperspec](https://www.lispworks.com/documentation/HyperSpec/Body/02_df.htm) doesn't require a backquote form must be interpreted as a S-exp representation. Quoting from Hyperspec > An implementation is free to interpret a backquoted form F1 as any form F2 that, when evaluated, will produce a result that is the same under equal as the result implied by the above definition, provided that the side-effect behavior of the substitute form F2 is also consistent with the description given above. The constructed copy of the template might or might not share list structure with the template itself. As an example, the above definition implies that > > `((,a b) ,c ,@d) > > will be interpreted as if it were > > (append (list (append (list a) (list 'b) 'nil)) (list c) d 'nil) > > but it could also be legitimately interpreted to mean any of the following: > > (append (list (append (list a) (list 'b))) (list c) d) > (append (list (append (list a) '(b))) (list c) d) > (list* (cons a '(b)) c d) > (list* (cons a (list 'b)) c d) > (append (list (cons a '(b))) (list c) d) > (list* (cons a '(b)) c (copy-list d)) Therefore, CL implementations may freely extend their backquote to support interpolation in other lexical syntax, without worrying introducing malformed syntax object. This stands in stark contrast to the wording in R6RS/R7RS-Small, which states: [R6RS 4.3.5](https://www.r6rs.org/final/html/r6rs/r6rs-Z-H-7.html#node_sec_4.3.5) > '<datum>‌‌ > `<datum>‌‌ > ,<datum>‌‌ > ,@<datum>‌‌ > #'<datum>‌‌ > #`<datum>‌‌ > #,<datum>‌‌ > #,@<datum>‌‌ > Each of these is an abbreviation: > ‌'<datum> for (quote <datum>), > ‌`<datum> for (quasiquote <datum>), > ‌,<datum> for (unquote <datum>), > ‌,@<datum> for (unquote-splicing <datum>), > ‌#'<datum> for (syntax <datum>), > ‌#`<datum> for (quasisyntax <datum>), > ‌#,<datum> for (unsyntax <datum>), and > ‌#,@<datum> for (unsyntax-splicing <datum> R7RS 4.2.8 > The two notations `〈qq template〉 and (quasiquote 〈qq template〉) are identical in all respects. ,〈expression〉 is identical to (unquote 〈expression〉), and ,@〈expression〉 is identical to (unquote-splicing 〈expression〉). The write procedure may output either format.
Member
Copy link

@readevalprintloop wrote in #312 (comment):

Therefore, CL implementations may freely extend their backquote to support interpolation in other lexical syntax, without worrying introducing malformed syntax object.

This is the behavior of TR7 (an R7RS implementation):

(define x 5)
(define rest '(100 200))
'`#u8(1 2 ,x ,@rest) ; => (apply bytevector `(1 2 ,x ,@rest))
(eval '`#u8(1 2 ,x ,@rest)) ; => #u8(1 2 5 100 200)
@readevalprintloop wrote in https://codeberg.org/scheme/r7rs/issues/312#issuecomment-13229283: > Therefore, CL implementations may freely extend their backquote to support interpolation in other lexical syntax, without worrying introducing malformed syntax object. This is the behavior of TR7 (an R7RS implementation): ```scheme (define x 5) (define rest '(100 200)) '`#u8(1 2 ,x ,@rest) ; => (apply bytevector `(1 2 ,x ,@rest)) (eval '`#u8(1 2 ,x ,@rest)) ; => #u8(1 2 5 100 200) ```

For TR7 behaviour, I swear that it is accidental. Just for fun I checked a more complex expression:

'`(list x #u8(1 2 ,x ,@rest) #(1 ,@rest x)) 
; => `(list x ,(apply bytevector `(1 2 ,x ,@rest)) ,(apply vector `(1 ,@rest x)))

The transformation is made during lexical read. At lexical level occurencies #( and #u8 appearing inside a quasi-quote are replaced by (unquote (apply ?vector (quasi-quote ...

For TR7 behaviour, I swear that it is accidental. Just for fun I checked a more complex expression: ```scheme '`(list x #u8(1 2 ,x ,@rest) #(1 ,@rest x)) ; => `(list x ,(apply bytevector `(1 2 ,x ,@rest)) ,(apply vector `(1 ,@rest x))) ``` The transformation is made during lexical read. At lexical level occurencies `#(` and `#u8` appearing inside a `quasi-quote` are replaced by `(unquote (apply ?vector (quasi-quote ...`
Member
Copy link

Trying to perform the transformation during lexical reading is doomed to fail.

Trying to perform the transformation during lexical reading is doomed to fail.

@mnw wrote in #312 (comment):

Trying to perform the transformation during lexical reading is doomed to fail.

I agree. Introducing special construct to handle manifest vector or bytevector as syntaxic constructs was a longer path in TR7. That explains but not excuses.

@mnw wrote in https://codeberg.org/scheme/r7rs/issues/312#issuecomment-13246296: > Trying to perform the transformation during lexical reading is doomed to fail. I agree. Introducing special construct to handle manifest vector or bytevector as syntaxic constructs was a longer path in TR7. That explains but not excuses.
Sign in to join this conversation.
No Branch/Tag specified
main
pages
proc-draft
c-trichotomy
global-id-prop-ref
allow-van-tonder-like-expanders
No results found.
Labels
Clear labels
Batteries
Issues for the Batteries volume of the report, providing a standard library
Cleanup
Issues where inconsistencies or other infelicities have arisen
Consent Docket
Items which will be automatically be given a certain resolution if no objection is made by a certain date
Environments
Issues for the Environments volume of the report, providing OS interfaces and similar
Foundations
Issues for the Foundations volume of the report, related to the core language semantics
Macrological Fascicle
Meta
Procedural and organisational issues
Multiple proposals exist
Multiple competing, concrete proposals to resolve this issue exist (whether they are SRFI or still only pre-SRFI)
Pie-in-the-sky ideas
Ideas currently lacking any form of worked-out proposal
Problems without solutions
Problems we *must* solve but haven’t worked out any solution for yet
Procedural Fascicle
Public Comment
Publications
Issues related to the practicalities of making the report available
Question
For information and clarification
R6RS Compatibility
Issue touches on questions of R6RS compatibility
R7RS-small
Issues with the Small language Report
Resolution in draft
A resolution (not necessarily final) is specified in the current draft of the relevant fascicle
Specification exists
Proposals for which a viable, but incomplete specification text exists
SRFI/RnRS spec exists
A SRFI for this proposal exists, or a solution from another RnRS
Milestone
Clear milestone
No items
No milestone
Projects
Clear projects
No items
No project
Assignees
Clear assignees
No assignees
7 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
scheme/r7rs#312
Reference in a new issue
scheme/r7rs
No description provided.
Delete branch "%!s()"

Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?