# Structs and Keywords

**URL:** <https://racket.discourse.group/t/structs-and-keywords/1399>\
**Category:** Questions & Answers\
**Created:** [October 17, 2022, 8:01am UTC](https://racket.discourse.group/t/structs-and-keywords/1399 "2022-10-17T08:01:10Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![denis](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/denis/32/786_2.png) [@denis](https://racket.discourse.group/u/denis)\
**Post date:** [October 17, 2022, 8:01am UTC](https://racket.discourse.group/t/structs-and-keywords/1399/1 "2022-10-17T08:01:10Z")

</div>

Coming back to learning Racket after a bit of a pause---I've just realized that Racket does not seem to have native support for keyword assignment in structs. Is that still the case? If so, I've read old [blog posts](https://www.greghendershott.com/2015/07/keyword-structs-revisited.html) about using macros to support this, and there seems to be the [struct++ library](https://docs.racket-lang.org/struct-plus-plus/index.html) that adds support for this. Anyway, I'm just wondering whether there are plans to add this to the base language? If not, are there specific reasons not to support this? Would it be safe to rely on custom macros or the struct++ library in this case? Or might that approach break with later updates to the core language?

---

<div class="post-metadata">

**Author:** ![soegaard](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/soegaard/32/19_2.png) [@soegaard](https://racket.discourse.group/u/soegaard)\
**Post date:** [October 17, 2022, 12:55pm UTC](https://racket.discourse.group/t/structs-and-keywords/1399/2 "2022-10-17T12:55:20Z")

</div>

I am a bit unsure what "keyword assignments" mean.  
Do you have a small example?

---

<div class="post-metadata">

**Author:** ![denis](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/denis/32/786_2.png) [@denis](https://racket.discourse.group/u/denis)\
**Post date:** [October 17, 2022, 1:14pm UTC](https://racket.discourse.group/t/structs-and-keywords/1399/3 "2022-10-17T13:14:49Z")

</div>

From the blog post linked above. Instead of using per-position assignment:

```nohighlight
(foo 10 ;a
     "foo" ;b
     13 ;c
     "bar" ;d
     "baz" ;e
     #f ;f
     "x" ;g
     42)

```

I'd like to use this:

```nohighlight
(foo #:a 10
     #:b "foo"
     #:c 13
     #:d "bar"
     #:e "baz"
     #:f #f
     #:g "x"
     #:h 42)

```

---

<div class="post-metadata">

**Author:** ![soegaard](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/soegaard/32/19_2.png) [@soegaard](https://racket.discourse.group/u/soegaard)\
**Post date:** [October 17, 2022, 7:38pm UTC](https://racket.discourse.group/t/structs-and-keywords/1399/4 "2022-10-17T19:38:42Z")

</div>

I think `struct++` is a good fit then.

---

<div class="post-metadata">

**Author:** ![jestarray](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/jestarray/32/433_2.png) [@jestarray](https://racket.discourse.group/u/jestarray)\
**Post date:** [October 19, 2022, 12:49am UTC](https://racket.discourse.group/t/structs-and-keywords/1399/5 "2022-10-19T00:49:23Z")

</div>

> <https://github.com/jeapostrophe/racket-langserver/issues/29>
>
> rust-analyzer provides a very neat feature in that it will print out virtual tex…t of struct field names and function param names when you call said function.
> 
> This feature would be a huge productivity gain for racket as structs will be much easier to work with, along with functions. Currently I create a lot of keyword functions for structs that are very large because I don't want to keep guessing what they are by position. (I have looked into the rebellion packages and etc but ehh, I prefer to minimize dependencies)
> 
> !\[image\](https://user-images.githubusercontent.com/34615798/108755300-eefb6c00-74fb-11eb-80bc-f7e9265a8ba2.png)
> 
> \`\`\`
> pub struct Rect {
> pub x: u32,
> pub y: u32,
> pub w: u32,
> pub h: u32,
> }
> impl Rect {
> pub fn new(x: u32, y: u32, w: u32, h: u32) -\> Self {
> Self { x, y, w, h }
> }
> }
> //so we can have a more ergonomic syntax for creating structs by position
> let r = Rect::new(0, 5, 32, 32);
> \`\`\`

Inlay hints can also fix this issue without language modifications! So if anyone knows how LSP works, please try and implement it, thanks.

---

<div class="post-metadata">

**Author:** ![timjervis](https://avatars.discourse-cdn.com/v4/letter/t/45deac/32.png) [@timjervis](https://racket.discourse.group/u/timjervis)\
**Post date:** [October 19, 2022, 4:38am UTC](https://racket.discourse.group/t/structs-and-keywords/1399/6 "2022-10-19T04:38:42Z")

</div>

If you have any appetite for roll-your-own macros, here's an outline of another approach. It looks long, but it's mostly tests.

```scheme
#lang racket

(require (for-syntax syntax/parse
                     racket/syntax))

(module+ test
  (require rackunit))

;;
;; Struct+
;;
(define-syntax (struct+ stx)

  ;; a syntax class for a struct field that associates a default or bespoke keyword 
  (define-syntax-class field
    (pattern (~or* [id:id kw-arg:keyword]
                   id:id)

             #:with kw
             (or (attribute kw-arg)
                 (string->keyword (symbol->string (syntax->datum (attribute id)))))
             
             #:with kw-header
             #`(kw id)))

  ;; The macro that creates a standard struct and a procedure to build it from keywords
  (syntax-parse stx
    [(_ struct-id:id
        [field:field ...+]
        (~optional (~seq #:new-name new-name)))

     #:fail-when (check-duplicate-identifier (syntax->list #'(field.id ...)))
     "duplicate field"

     #:with new-id
     (or (attribute new-name)
         (format-id (attribute struct-id) "new-~a" (attribute struct-id)))

     #`(begin
         (struct struct-id
           [field.id ...])

         (define (new-id (~@ . field.kw-header) ...)
           (struct-id field.id ...)))]))

;;
;; Tests
;;

(module+ test
  (struct+ tester
           [field-1
            [field-2 #:bespoke-keyword]])
  
  (define t
    (new-tester #:field-1 "foo"
                #:bespoke-keyword "bar"))

  (check-equal? (tester-field-1 t)
                "foo")

  (check-equal? (tester-field-2 t)
                "bar")

  (struct+ tester-2
           [foo
            bar]
           #:new-name bespoke-name-for-new)
  
  (define s
    (bespoke-name-for-new #:foo "hello"
                          #:bar "world"))

  (check-equal? (tester-2-foo s)
                "hello")

  (check-equal? (tester-2-bar s)
                "world")

  (struct+ foo
           [a
            b
            c
            d
            e
            f
            g
            h])
  
  (define f
    (new-foo #:a 10
             #:b "foo"
             #:c 13
             #:d "bar"
             #:e "baz"
             #:f #f
             #:g "x"
             #:h 42))
  
  (check-equal? (foo-a f)
                10)
  (check-equal? (foo-b f)
                "foo")
  (check-equal? (foo-c f)
                13)
  (check-equal? (foo-d f)
                "bar")
  (check-equal? (foo-e f)
                "baz")
  (check-equal? (foo-f f)
                #f)
  (check-equal? (foo-g f)
                "x")
  (check-equal? (foo-h f)
                42))

```

---

<div class="post-metadata">

**Author:** ![Laurent.O](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/laurent.o/32/18_2.png) [@Laurent.O](https://racket.discourse.group/u/Laurent.O)\
**Post date:** [October 19, 2022, 3:32pm UTC](https://racket.discourse.group/t/structs-and-keywords/1399/7 "2022-10-19T15:32:08Z")

</div>

As only a minor improvement, note that you can also write `#;` comments, which makes it look more like keywords, and works with one liners:

```scheme
(foo #;a 10 #;b "foo" #;c 13)

```

(except of course for Discourse which doesn't handle this type of comments.)

---

<div class="post-metadata">

**Author:** ![denis](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/denis/32/786_2.png) [@denis](https://racket.discourse.group/u/denis)\
**Post date:** [October 25, 2022, 7:31am UTC](https://racket.discourse.group/t/structs-and-keywords/1399/8 "2022-10-25T07:31:50Z")

</div>

Thanks for all the very helpful answers. Seems there are a couple of ways to support this---which is good to know.

Does anyone know why this is not a standard feature?

---

<div class="post-metadata">

**Author:** ![greghendershott](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/greghendershott/32/98_2.png) [@greghendershott](https://racket.discourse.group/u/greghendershott)\
**Post date:** [October 26, 2022, 12:41am UTC](https://racket.discourse.group/t/structs-and-keywords/1399/9 "2022-10-26T00:41:49Z")

</div>

On the one hand Racket aims to be "batteries included". Of course, different people, writing different kinds of programs, can have very different ideas what specific set of batteries would be ideal for them.

On the other hand, Racket macros mean that, if a "battery" isn't standard, you can almost always make it yourself -- including even features that would need to be built into, and require an updated version of, most other programming languages. The self-service lane is always open.

For example I don't tend to write many programs that use many "fat" structs; not that it's wrong to do so, I just haven't happened to be doing things for problem spaces where I need that very often. When I do need such a struct, I'm fine writing a custom constructor "by hand", which takes keyword arguments. If I needed it more often, for some project, I'd be OK writing the sort of little definer macro I outlined in that old blog most of mine that you linked to. Or using a package like `struct++`.

So I don't have a good answer for why this specific thing isn't standard in Racket, at least not yet. But I think the good news is, Racket in general has a wonderful answer, which is that you're empowered to add the missing thing, in a way that you might be unable to do in many languages.

---

<div class="post-metadata">

**Author:** ![soegaard](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/soegaard/32/19_2.png) [@soegaard](https://racket.discourse.group/u/soegaard)\
**Post date:** [October 26, 2022, 11:14am UTC](https://racket.discourse.group/t/structs-and-keywords/1399/10 "2022-10-26T11:14:57Z")

</div>

> [@denis](#):
>
> Does anyone know why this is not a standard feature?

Partly it is historical. Racket evolved from Scheme which has neither structures/records nor keywords. Most Scheme implementations had nevertheless structures in some form, so a design was made without keywords.

It was relatively late that keywords were introduced in Racket:

[Keyword and Optional Arguments in PLT Scheme, Flatt and Barzilay  
 ](https://www.cs.utah.edu/plt/publications/scheme09-fb.pdf)

This doesn't answer the obvious question: why wasn't the strucure constructors updated to allow keyword arguments?

There are several factors, but I believe performance might have been an important one.  
Calling a function with keyword arguments are slower than calling an function that doesn't allow keywords. Some of the bookkeeping needs to happen at runtime. This is in contrast to statically typed languages, which can handle this at compile time.

---

<div class="post-metadata">

**Author:** ![hendrikboom3](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/hendrikboom3/32/2748_2.png) [@hendrikboom3](https://racket.discourse.group/u/hendrikboom3)\
**Post date:** [October 26, 2022, 12:51pm UTC](https://racket.discourse.group/t/structs-and-keywords/1399/11 "2022-10-26T12:51:38Z")

</div>

So in principle, this should be possible at compile time in typed Racket.

-- hendrik
