Why not remove the `recordFactory` type then? type Record interface { Key() string Marshal() []byte Unmarshal([]byte) error }
func put[R Record](db *SimpleDatabase, t UpdateTransaction, v R) error { k := v.Key() b := v.Marshal() if err := db.put(t, k, b); err != nil { return fmt.Errorf("db error: %w", err) } return nil } You might need to do the Pointer Receiver Trick (not sure if there's an FAQ entry to link to here, so I'll just link to my not super great talk <https://youtu.be/QP6v-Q5Foek?t=2718> explaining it) to implement get, i.e.: type Record[T any] interface { *T Key() string Marshal() []byte Unmarshal([]byte) error } func get[R any, PR[R]](db *SimpleDatabase, t ViewTransaction) (R, error) { var r R k := PR(&r).Key() b, err := db.get(t, k) if err != nil { return r, err } err = PR(&r).Unmarshal(b) return r, err } (Note: This assumes that the `Key() string` method is still stateless, but ISTM your factory type assumes that as well?) On Fri, Jul 28, 2023 at 10:48 AM Salvatore Domenick Desiano < neard...@gmail.com> wrote: > I guess I didn't ask an actual question... is there a more idiomatic way > of doing this? > > Thank you! > > -- Salvatore > smile. > > > On Monday, July 24, 2023 at 4:00:59 PM UTC-4 Salvatore Domenick Desiano > wrote: > >> Ever since 1.18 came out I've been struggling to find an idiomatic way to >> implement a certain kind of adapter. I'm starting to suspect I'm Holding It >> Wrong (TM) so here I am. >> >> In short, I have an adapter that is effectively a collection of static >> methods and it doesn't look right. >> >> Imagine you have a generic key-value database (transaction and life-cycle >> methods omitted for brevity): >> >> type SimpleDatabase interface { >> Get(t ViewTransaction, key string) ([]byte, bool, error) >> Put(t UpdateTransaction, key string, value []byte) error >> } >> >> You then want to build something more specific on top using generics to >> avoid repetition of tricky code: >> >> type ApplicationDatabase struct { >> impl *SimpleDatabase >> } >> >> func (db *ApplicationDatabase) PutProjectRecord(t UpdateTransaction, r >> *ProjectRecord) error { >> return put[ProjectRecord](db.impl, projectRecordFactory, t, r) >> } >> >> type recordFactory[V any] interface { >> dbKey(value V) string >> encodeValue(value V) []byte >> decodeValue(data []byte) (V, error) >> } >> >> func put[V any](db *SimpleDatabase, f *recordFactory[V], t >> UpdateTransaction, value V) error { >> key := f.dbKey(value) >> valueBytes := f.encodeValue(value) >> if err := db.put(t, key, valueBytes); err != nil >> return fmt.Errorf("db error: %w", err) >> } >> return nil >> } >> >> This is nice and clean -- adding a new record type is just a matter of >> adding a new recordFactory implementation. Adding more complexity to the >> put/get (recovery, etc.) only has to happen once. The part that bugs me, >> though, is that the implementation has no state and really looks like it >> shouldn't be instantiated: >> >> type projectRecordFactory struct{} >> func (f *projectRecordFactory) dbKey() string { return .... } >> func (f *projectRecordFactory) encodeValue(value V) []byte { >> json.Marshal(...) } >> func (f *projectRecordFactory) decodeValue(data []byte) (V, error) { >> json.Unmarshal(...) } >> >> and, frankly, if I didn't instantiate it, it would feel like either Java >> or C++ metaprogramming. >> >> I'm open to a completely different structure but I am really hoping to >> add new record types by adding a new package or type. >> >> Thanks! >> >> -- Salvatore >> smile. >> >> -- > You received this message because you are subscribed to the Google Groups > "golang-nuts" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to golang-nuts+unsubscr...@googlegroups.com. > To view this discussion on the web visit > https://groups.google.com/d/msgid/golang-nuts/318e98c2-d13e-4188-b65b-bbc7644e3ab7n%40googlegroups.com > <https://groups.google.com/d/msgid/golang-nuts/318e98c2-d13e-4188-b65b-bbc7644e3ab7n%40googlegroups.com?utm_medium=email&utm_source=footer> > . > -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/CAEkBMfFLJYt9N8fvMALfthk-s7q2WTE3yKx95u7puwDEfA5p5g%40mail.gmail.com.