jcpetruzza / barbies

BSD 3-Clause "New" or "Revised" License
92 stars 15 forks source link

Field name API? #23

Open fumieval opened 4 years ago

fumieval commented 4 years ago

Being able to obtain field names of a record is pretty useful. The following code works but unfortunately GHC can't derive Generic1 instances of higher-kinded types... Do you think barbies' hacks can be applied here?

class FieldNamesB b where
  bfieldNames :: b (Const String)

instance FieldNamesB U1 where
  bfieldNames = U1

instance (FieldNamesB f, FieldNamesB g) => FieldNamesB (f :*: g) where
  bfieldNames = bfieldNames :*: bfieldNames

instance (FieldNamesB t, c ~ 'MetaSel ('Just name) su ss dl, KnownSymbol name, t ~ Const String) => FieldNamesB (M1 S c t) where
  bfieldNames = M1 (Const (fromString (symbolVal (Proxy :: Proxy name))))

instance (FieldNamesB t) => FieldNamesB (M1 C c t) where
  bfieldNames = M1 bfieldNames

instance (FieldNamesB t) => FieldNamesB (M1 D c t) where
  bfieldNames = M1 bfieldNames
jcpetruzza commented 4 years ago

Indeed, something like that would be quite useful. I imagine I'd define the API more or less like this:

class FunctorB b => RecordB b where
  baddSelectors :: b f -> b (Const String `Product` f)

bselectors :: (ApplicativeB b, RecordB b) =>  b (Const String)
bselectors = bmap first $ baddSelectors (bpure Unit)

So bselectors is your bfieldNames, but one can also use baddSelectors on sum-types, just like we do with baddDicts and bdicts (btw, ApplicativeB will replace ProductB in the next release, I'm using it here since that change is already on master).

I may add something like that at some point, but if in the meantime you (or someone else) wants to give a shot, one can look at the code for the generic implementation of baddDicts for inspiration, it should be relatively similar.