# How can I export the struct definition but not the constructor?

**URL:** https://racket.discourse.group/t/how-can-i-export-the-struct-definition-but-not-the-constructor/1069
**Category:** Questions & Answers
**Created:** [June 8, 2022, 7:31pm UTC](https://racket.discourse.group/t/how-can-i-export-the-struct-definition-but-not-the-constructor/1069 "2022-06-08T19:31:30Z")
**Posts on this page:** 4
**Page:** 1

<div class="post-metadata">

### Author: ![dstorrs](https://avatars.discourse-cdn.com/v4/letter/d/898d66/32.png) [@dstorrs](https://racket.discourse.group/u/dstorrs)
#### Post date: [June 8, 2022, 7:31pm UTC](https://racket.discourse.group/t/how-can-i-export-the-struct-definition-but-not-the-constructor/1069/1 "2022-06-08T19:31:30Z")

</div>

The goal is to have a struct type defined in one file and other files can use the struct (via accessors and `match`) but cannot create instances of the struct. Is there a way to do this aside from using `#:constructor-name` to separate the transformer binding and the constructor? This is relevant to code that I'm writing with `struct-plus-plus`.

```scheme
; person.rkt
(provide get-person <X>) ; what should <X>? be?

(struct person (name))
(define (get-person db-id) ...query the database, return a `person` struct...)

; invitations.rkt
(require "person.rkt")
(match (get-person 7) ; this works
  [(person name) name])

; this throws an error
(person 'bob) ; error! constructor was not provided from person.rkt

```

---

<div class="post-metadata">

### Author: ![sorawee](https://avatars.discourse-cdn.com/v4/letter/s/ea5d25/32.png) [@sorawee](https://racket.discourse.group/u/sorawee)
#### Post date: [June 8, 2022, 8:26pm UTC](https://racket.discourse.group/t/how-can-i-export-the-struct-definition-but-not-the-constructor/1069/2 "2022-06-08T20:26:08Z")

</div>

What’s the reason you don’t want to use `#:constructor-name`. It sounds like a perfect solution for this.

But in case you really don’t want to use `#:constructor-name`, you can use the technique I described in [https://racket.discourse.group/t/impersonate-syntax-transformer-cursed-or-not/971](https://racket.discourse.group/t/impersonate-syntax-transformer-cursed-or-not/971). Following results in a run-time error (which seems to be what you want -- but let me know if you want a compile-time error. I can do that too)

```scheme
#lang racket

(module provider racket
  (require syntax/parse/define
           (for-syntax racket/struct-info))

  (provide get-person
           (rename-out [person* person]))

  (struct person (name))

  (define (get-person db-id)
    (person "foo"))

  (define-syntax-parse-rule (repack x:id constr new-id:id)
    (define-syntax new-id
      (impersonate-procedure
       (syntax-local-value #'x)
       (λ (y)
         (values (syntax-parser [(_ . args) #'(constr . args)]
                                [_:id #'constr])
                 y)))))

  (define better-person
    (procedure-rename
     (λ (name)
       (error "constructor was not provided"))
     'person))

  (repack person better-person person*))

(require 'provider)

(match (get-person 7) ; this works
  [(person name) name])

; this throws an error
(person 'bob) ; constructor was not provided

```

---

<div class="post-metadata">

### Author: ![dstorrs](https://avatars.discourse-cdn.com/v4/letter/d/898d66/32.png) [@dstorrs](https://racket.discourse.group/u/dstorrs)
#### Post date: [June 8, 2022, 8:43pm UTC](https://racket.discourse.group/t/how-can-i-export-the-struct-definition-but-not-the-constructor/1069/3 "2022-06-08T20:43:23Z")

</div>

> [@sorawee](#):
>
> What’s the reason you don’t want to use `#:constructor-name`. It sounds like a perfect solution for this.

My `struct-plus-plus` module provides a very compact form for declaring a struct and a lot of scaffolding around it. Examples includes a keyword constructor which checks contracts and business logic, potentially transforms values, installs wrappers, etc. The problem is that I've never wanted to use different constructor names before so I didn't consider it when I was writing spp, meaning that right now it assumes the constructor name is the default value, so the code that it generates for the keyword constructor would end up failing.

```scheme
(require struct-plus-plus)
(struct++ person ([name (or/c non-empty-string? symbol?) ~a]
                  [(age 18) positive?]) 
          #:transparent)

(person++ #:name "alice" #:age 27) ; => (person "alice" 27)
(person++ #:name 'bob) ; => (person "bob" 18), because the ~a wrapper forced the name to string and age defaulted
(person++ #:name #f #:age 12) ; => runtime error. name must be non-empty-string? or symbol?

(struct++ animal (species) ; contracts and wrappers are optional
          #:constructor-name make-animal
          #:transparent)

(animal++ #:species "dog")  
; /Users/dstorrs/test.rkt:17:10: animal: bad syntax;                                   
; identifier for static struct-type information cannot be used as an expression                              
; in: animal  

; fails because spp assumed that Racket's default constructor was 'animal'

```

I figured I'd ask if there's an easy way to do this before I started looking at rewriting parts of spp.

---

<div class="post-metadata">

### Author: ![capfredf](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/capfredf/32/935_2.png) [@capfredf](https://racket.discourse.group/u/capfredf)
#### Post date: [June 14, 2022, 4:13pm UTC](https://racket.discourse.group/t/how-can-i-export-the-struct-definition-but-not-the-constructor/1069/4 "2022-06-14T16:13:22Z")

</div>

By default, a struct name plays two roles: a transformer that contains the structure's static info and its constructor. So off top of my head, I can't think of a way to do this without giving a different name to the constructor (via `#:constructor-name`) or the struct (via `#:name`).
