[PATCH] records: match-record supports specifying a different variable name.

  • Done
  • quality assurance status badge
Details
2 participants
  • Attila Lendvai
  • Ludovic Courtès
Owner
unassigned
Submitted by
Attila Lendvai
Severity
normal
A
A
Attila Lendvai wrote on 20 Dec 2022 18:40
(address . guix-patches@gnu.org)(name . Attila Lendvai)(address . attila@lendvai.name)
20221220174018.14433-1-attila@lendvai.name
An example:

(match-record obj <my-type>
(field1 (field2 custom-var-name) field3)
...)
---
guix/records.scm | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)

Toggle diff (27 lines)
diff --git a/guix/records.scm b/guix/records.scm
index 13463647c8..0b2487a156 100644
--- a/guix/records.scm
+++ b/guix/records.scm
@@ -592,13 +592,16 @@ (define-syntax lookup-field
(define-syntax match-record-inner
(lambda (s)
(syntax-case s ()
- ((_ record type (field rest ...) body ...)
- #`(let-syntax ((field-offset (syntax-rules ()
+ ((_ record type ((field-name variable-name) rest ...) body ...)
+ #'(let-syntax ((field-offset (syntax-rules ()
((_ f)
- (lookup-field field 0 f)))))
+ (lookup-field field-name 0 f)))))
(let* ((offset (type map-fields field-offset))
- (field (struct-ref record offset)))
+ (variable-name (struct-ref record offset)))
(match-record-inner record type (rest ...) body ...))))
+ ((_ record type (field rest ...) body ...)
+ ;; Redirect to the canonical form above.
+ #'(match-record-inner record type ((field field) rest ...) body ...))
((_ record type () body ...)
#'(begin body ...)))))
--
2.35.1
L
L
Ludovic Courtès wrote on 21 Dec 2022 23:15
(name . Attila Lendvai)(address . attila@lendvai.name)(address . 60225@debbugs.gnu.org)
871qos4d42.fsf@gnu.org
Hi,

Attila Lendvai <attila@lendvai.name> skribis:

Toggle quote (6 lines)
> An example:
>
> (match-record obj <my-type>
> (field1 (field2 custom-var-name) field3)
> ...)

Nice! It looks like a useful extension to me.

Toggle quote (5 lines)
> - ((_ record type (field rest ...) body ...)
> - #`(let-syntax ((field-offset (syntax-rules ()
> + ((_ record type ((field-name variable-name) rest ...) body ...)
> + #'(let-syntax ((field-offset (syntax-rules ()

I’d just drop ‘-name’, it’s all names anyway. :-)

Could you also add a test in ‘tests/records.scm’ next to the others?

TIA,
Ludo’.
A
A
Attila Lendvai wrote on 22 Dec 2022 03:14
[PATCH v2 1/2] records: match-record supports specifying a different variable name.
(address . 60225@debbugs.gnu.org)(name . Attila Lendvai)(address . attila@lendvai.name)
20221222021455.18632-1-attila@lendvai.name
An example:

(match-record obj <my-type>
(field1 (field2 custom-var-name) field3)
...)

* guix/records.scm (match-record-inner): Add support for the new syntax.
* tests/records.scm ("match-record, simple"): Add a simple test case for the
new syntax.
---
guix/records.scm | 9 ++++++---
tests/records.scm | 4 ++--
2 files changed, 8 insertions(+), 5 deletions(-)

Toggle diff (41 lines)
diff --git a/guix/records.scm b/guix/records.scm
index 13463647c8..1f097c7108 100644
--- a/guix/records.scm
+++ b/guix/records.scm
@@ -592,13 +592,16 @@ (define-syntax lookup-field
(define-syntax match-record-inner
(lambda (s)
(syntax-case s ()
- ((_ record type (field rest ...) body ...)
- #`(let-syntax ((field-offset (syntax-rules ()
+ ((_ record type ((field variable) rest ...) body ...)
+ #'(let-syntax ((field-offset (syntax-rules ()
((_ f)
(lookup-field field 0 f)))))
(let* ((offset (type map-fields field-offset))
- (field (struct-ref record offset)))
+ (variable (struct-ref record offset)))
(match-record-inner record type (rest ...) body ...))))
+ ((_ record type (field rest ...) body ...)
+ ;; Redirect to the canonical form above.
+ #'(match-record-inner record type ((field field) rest ...) body ...))
((_ record type () body ...)
#'(begin body ...)))))
diff --git a/tests/records.scm b/tests/records.scm
index 8504c8d5a5..b1203dfeb7 100644
--- a/tests/records.scm
+++ b/tests/records.scm
@@ -540,8 +540,8 @@ (define-record-type* <foo> foo make-foo
(first second)
(list first second))
(match-record (foo (first 'a) (second 'b)) <foo>
- (second first)
- (list first second)))))
+ (second (first first/new-var))
+ (list first/new-var second)))))
(test-equal "match-record, unknown field"
'syntax-error
--
2.35.1
A
A
Attila Lendvai wrote on 22 Dec 2022 03:14
[PATCH v2 2/2] tests: records: Add a failing test for match-record.
(address . 60225@debbugs.gnu.org)(name . Attila Lendvai)(address . attila@lendvai.name)
20221222021455.18632-2-attila@lendvai.name
* tests/records.scm ("match-record, syntactic interference"): New failing test.
---

i'm not sure what's going on here, but it looks like a bug to me.

i've experienced this in real code, and the error message was
not very helpful.

tests/records.scm | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)

Toggle diff (34 lines)
diff --git a/tests/records.scm b/tests/records.scm
index b1203dfeb7..8a48e2fd07 100644
--- a/tests/records.scm
+++ b/tests/records.scm
@@ -561,4 +561,27 @@ (define-record-type* <foo> foo make-foo
(make-fresh-user-module)))
(lambda (key . args) key)))
+(test-expect-fail 1)
+(test-equal "match-record, syntactic interference"
+ '(1 2)
+ (begin
+ (define* (make-form #:optional (bindings '()))
+ `(begin
+ (use-modules (guix records))
+
+ (let ((first 42)) ; here it does not interfere
+ (define-record-type* <foo> foo make-foo
+ foo?
+ (first foo-first (default 1))
+ (second foo-second))
+
+ (let (,@bindings) ; but here it does interfere
+ (match-record (foo (second 2)) <foo>
+ (first second)
+ (list first second))))))
+ ;; This works fine.
+ (eval (make-form) (make-fresh-user-module))
+ ;; But this fails, although I think it shouldn't.
+ (eval (make-form '((second 43))) (make-fresh-user-module))))
+
(test-end)
--
2.35.1
L
L
Ludovic Courtès wrote on 27 Dec 2022 23:49
Re: bug#60225: [PATCH] records: match-record supports specifying a different variable name.
(name . Attila Lendvai)(address . attila@lendvai.name)(address . 60225-done@debbugs.gnu.org)
87wn6ca2db.fsf_-_@gnu.org
Hi,

Attila Lendvai <attila@lendvai.name> skribis:

Toggle quote (10 lines)
> An example:
>
> (match-record obj <my-type>
> (field1 (field2 custom-var-name) field3)
> ...)
>
> * guix/records.scm (match-record-inner): Add support for the new syntax.
> * tests/records.scm ("match-record, simple"): Add a simple test case for the
> new syntax.

Applied, thanks!

Ludo’.
Closed
L
L
Ludovic Courtès wrote on 27 Dec 2022 23:53
(name . Attila Lendvai)(address . attila@lendvai.name)(address . 60225@debbugs.gnu.org)
87sfh0a27c.fsf_-_@gnu.org
Attila Lendvai <attila@lendvai.name> skribis:

Toggle quote (5 lines)
> * tests/records.scm ("match-record, syntactic interference"): New failing test.
> ---
>
> i'm not sure what's going on here, but it looks like a bug to me.

[...]

Toggle quote (5 lines)
> + (let (,@bindings) ; but here it does interfere
> + (match-record (foo (second 2)) <foo>
> + (first second)
> + (list first second))))))

This has to do with how macro “literals” are matched (info "(guile)
Syntax Rules"):

A literal matches an input expression if the input expression is an
identifier with the same name as the literal, and both are unbound(1).

Although literals can be unbound, usually they are bound to allow
them to be imported, exported, and renamed. *Note Modules::, for more
information on imports and exports. In Guile there are a few standard
auxiliary syntax definitions, as specified by R6RS and R7RS:

In the example above, the ‘let’ binding for ‘second’ was shadowing the
other ‘second’.

(I think this was recently discussed on guix-devel or something.)

Ludo’.
?