Hi everyone,
I am wondering if anyone else has run into this issue and if there is some commonly-used work-around?
The issue is the name of my struct and the name of a binding for an instance of the struct usually ends up having the same name which causes issues. For example:
(struct person
(name age))
(define (person-update-age person new-age)
(struct-copy person person
[age new-age]))
The problem is I would like to name the parameter to the person-update-age
also person. This then shadows the person
struct-id and causes the code to raise an error.
I was thinking of adding '$' to my struct names to avoid this conflict. For example:
(struct $person
(name age))
(define (person-update-age person new-age)
(struct-copy $person person
[age new-age]))
This seems to work ok, but I would like to check if there is already a community-established convention for this situation?
Regards
This is a FAQ. See a recent discussion at https://racket.discourse.group/t/choosing-good-names/2900 .
TL;DR: some people use a-person
/ the-person
for an instance of person
. Some people abbreviate instance names. But there’s not really a “community-established convention”.
1 Like
I've seen the a- / the- notation used in a somewhat readable formal semantics system.
It used 'a-' when introducing a parameter (binding it), and 'the' when referring to it.
eg.
To increment a number
add one to the number.
Unfortunately, 'a-person' and 'the-person' are recognised in Racket
as having nothing whatsoever to do with each other.
-- hendrik
One thought: if you're writing these update/set functions yourself, you're doing more work than you need to. Check struct-plus-plus.
#lang racket
(require struct-plus-plus)
(struct++ person (name age) #:transparent)
(define bob (person 'bob 17))
bob
; Displays: (person 'bob 17)
(set-person-age bob 18) ; functional update, not mutational
; Displays: (person 'bob 18)
(set-person-age bob (add1 (person.age bob)))
; Displays: (person 'bob 18)
(update-person-age bob (λ (current-val) (+ 10 current-val)))
; Displays: (person 'bob 27)
; Also, keyword constructor and all fields have setters/updaters
(person++ #:name 'bob #:age 37)
; Displays: (person 'bob 37)
(set-person-name bob 'tom)
; Displays: (person 'tom 17)
If we want, we can make the-number
refer to a-number
by defining a custom top
.
#lang racket/base
(require (for-syntax racket/base racket/syntax syntax/parse))
(provide (rename-out [my-top #%top]))
(define-syntax (my-top stx)
(syntax-parse stx
[(_my-top . id:id)
(define str (symbol->string (syntax-e #'id)))
(define n (string-length str))
(if (and (>= n 4)
(string=? (substring str 0 4) "the-"))
(with-syntax ([a (format-id #'id "a-~a" (substring str 4 n))])
(syntax/loc #'id a))
(syntax/loc stx
(#%top . id)))]))
Now the following program works:
(require "the-top.rkt")
(define (subscript a-string a-number)
(~a the-string "_" the-number))
(subscript "foo" 42)
2 Likes
Use p
as short for the person.
Note that field accessors are prefixed with the struct name (here person
).
Thus in an expression like (person-age p)
no one doubts that p
is a person.
Thanks everyone,
The person-update-age
function is just for the example. I do not write those types of functions
I like the a-person
convention for the binding. I will try a couple of things and see what works for me.
Regards
Interesting. I had no idea that was possible.
It's worth further study.
Do I presume correctly that it does not restrict the 'the-' form to binding
occurrences and the 'a-' form to bound occurrances?
-- hendrik
Do I presume correctly that it does not restrict the 'the-' form to binding
occurrences and the 'a-' form to bound occurrances?
Consider the example:
(define (foo a-number)
(+ the-number 1))
Here the-number
is unbound, so the expander expands into (#%top . the-number)
.
Since we imported #%top
from a module that exported my-top
as #%top
,
the expander continues looking at (my-top . the-number)
.
The transformer of my-top
looks at the identifier. If it begins with the-
it rewrites it to an identifier that begins with a-
instead. Here it becomes a-number
(and has the same context as the original identifier). If the identifier doesn't begin with the-
it just leaves it as-is.
In short, all unbound references caused by identifiers of the form the-x
are rewritten into references of the form a-x
.
Of course I meant to have written this the other way around:
Do I presume correctly that it does not restrict the 'a-' form to binding
occurrences and the 'the-' form to bound occurrances?
-- hendrik
Actually after playing around with different options, adding a '^' suffix for structs feels better and I will switch to that as a convention.
Do I presume correctly that it does not restrict the 'a-' form to binding
occurrences and the 'the-' form to bound occurrances?
Well, the macro does nothing more than:
In short, all unbound references caused by identifiers of the form the-x
are rewritten into references of the form a-x
.
So an a-
identifier behaves as normal. Wrt. the-
identifiers. Try this:
(define (foo a-number x)
(let ([the-number 42])
(+ x the-number)))
(foo 1 2)
I expect to see 44 as result.
Nooooooo
Dammit, thanks for letting me know.
I will now choose a different suffix and update a lot of code
I have changed my suffix to '.' so my struct ids now look like this:
(struct person.
...)