How to implement generic method implementation separate from struct definition

I'm wondering if it is possible to do the following:

Define a struct in 1 file:

---[ data.rkt ]---

(struct person
  (name age))

Define an interface in a separate file:

---[ greetable.rkt ]---

(define-generics greetable
  (greet greetable))

And in a 3rd file provide the implementation of the greetable interface for the person struct:

---[ person-greetable.rkt ]---

(require 
  "data.rkt"
  "greetable.rkt")

; Somehow provide the implementation maybe using something like: 
; (make-generic-struct-type-property)

Hi, @rvdalen.

I haven't used generics at all, so please don't consider my reply an "answer", it is as much for my own edification as anything.

Is this sort of what you have in mind?

#lang racket

(module data racket
  (provide (struct-out person))
  
  (struct person [name age]
    #:transparent))

(module iface racket
  (require racket/generic)

  (provide greet gen:greetable)
  
  (define-generics greetable
    (greet greetable)))

(require 'data
         'iface
         racket/generic)

(define prop:greets
  (make-generic-struct-type-property
   gen:greetable
   (define (greet person)
     (format "Hi there, ~a!" (person-name person)))))

(struct person* person []
  #:transparent
  #:property
  prop:greets #false)
  
(greet (person* "john" 31))

Yes something like that, but without the derived person* struct, the generic interface must be implemented for the person struct:)

Struct cannot be changed in another module. But you may use external generics. For example, from gls package.

#lang racket

(module data racket
  (provide (struct-out person))
  
  (struct person [name age]
    #:transparent))

(module iface racket
  (require gls)

  (provide greet)
  
  (defgeneric greet))

(require 'data
         'iface
         gls)

(add-method greet
  (method ([person person?]) (format "Hi there, ~a!" (person-name person))))
  
(greet (person "john" 31))
1 Like

Ah, thanks so much for mentioning GLS. I will look into GLS. It seems as you mentioned there is no built in way to do this with base Racket.