VinylRecords / Vinyl

Extensible Records for Haskell. Pull requests welcome! Come visit us on #vinyl on freenode.
http://hackage.haskell.org/package/vinyl
MIT License
260 stars 49 forks source link

Vinyl to plain record conversion #124

Open gagandeepb opened 5 years ago

gagandeepb commented 5 years ago

I was wondering if a generic Vinyl to plain record conversion function is something useful (either as a PR to Vinyl(in code/docs form) or as a separate extension library) ? For example, to go from Vinyl's ElField interpreted records to plain record:

import           Data.Vinyl
import qualified GHC.Generics as G
import qualified Generics.SOP as S
import qualified Data.Vinyl.Functor as VF

-- source form
r1 :: Rec ElField '[("age" ::: Int), ("iscool" ::: Bool), ("yearbook" ::: Text)]
r1 = xrec (23, True, "You spin me right round")

-- the target form
data TargetForm = T { age :: Int, iscool :: Bool, yearbook :: Text} deriving (Show, G.Generic)

instance S.Generic TargetForm

class Vinyl2Plain rs a where 
  plainize :: Rec VF.ElField rs -> a 

instance (Generic a, StripFieldNames rs, 
  Code a ~ '[rs'], (Unlabeled rs) ~ rs') => Vinyl2Plain rs a where 
  plainize vinylRec = to (SOP (Z $ helper (stripNames vinylRec)))
    where 
      helper :: Rec VF.Identity rs -> NP I rs
      helper RNil = Nil
      helper ((VF.Identity x) :& rest) = (I x) :* (helper rest)

go :: TargetForm
go = plainize r1

As you can see, the Vinyl2Plain instance is generic over all possible Rec ElField's; all it needs is a matching target form in current scope. One use case I can think of is serializing a Rec to JSON: once the Rec is converted to a plain record, writing a ToJSON instance is straightforward via the Generics route.

I'd be very curious to hear your views on the above.

acowley commented 5 years ago

I think this can be a very useful feature! I've not figured out where the vinyl-to-JSON things should live due to the aeson dependency, but perhaps this would have a smaller footprint.

Alternately, we could do a vinyl-generics package and put things in there to keep the core vinyl package dependency chain limited to what ships with GHC.

Your contribution here can have significance -- beyond JSON -- for any occasion when you might use vinyl for intermediate data structures as you add and remove fields, then you want to transform your data back into plain records for an API access point or something.

Do you have any thoughts about a separate vinyl-generics package versus putting more into the main package? I'm trying to weigh the choices, and would appreciate input.

gagandeepb commented 5 years ago

Thanks for your encouraging thoughts! I think a separate vinyl-generics package could provide a more flexible space for certain slightly-experimental/exploratory utilities for working with vinyl records, such as the one I've described above. This would also allow the vinyl-generics package to be (probably) less constrained in choosing its dependencies, as compared to the main package. Just my opinion.

acowley commented 5 years ago

I'm glad you're of that mind, as that's the way I was leaning, too. Let's kick it off with this and the direct vinyl-to-JSON code.

sboosali commented 5 years ago

I've played around with converting product types to and from Vinyl Records, and defining json instances for any vinyl rec. I can find those when I get back home.

actual libraries that do these things of similar, which you might want to look at, are:

On Tue, Sep 18, 2018, 14:06 Anthony Cowley notifications@github.com wrote:

I'm glad you're of that mind, as that's the way I was leaning, too. Let's kick it off with this and the direct vinyl-to-JSON code.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/VinylRecords/Vinyl/issues/124#issuecomment-422554836, or mute the thread https://github.com/notifications/unsubscribe-auth/ACNoMbQ0x-g5UhFMbA0bvpzhkR_afRfaks5ucWBHgaJpZM4WuCsp .

acowley commented 5 years ago

For reference, the JSON code I wanted to include in this new package is one of these variants, but I was unaware of that composite-aeson package, so I will take a look to see if there’s anything gained with the new code.

Thank you for bringing up these packages!

sboosali commented 5 years ago

fwiw, we should define toEncoding too or efficiency, since we only have do it in a few places.

http://hackage.haskell.org/package/aeson-1.4.0.0/docs/Data-Aeson-Types.html#v:toEncoding

On Tue, Sep 18, 2018, 15:56 Anthony Cowley notifications@github.com wrote:

For reference, the JSON code I wanted to include in this new package is one of these variants https://github.com/VinylRecords/Vinyl/blob/master/tests/Aeson.hs.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/VinylRecords/Vinyl/issues/124#issuecomment-422585772, or mute the thread https://github.com/notifications/unsubscribe-auth/ACNoMS8RedyE0rRgsUHcqlm0zZ9qjDm9ks5ucXorgaJpZM4WuCsp .

gagandeepb commented 5 years ago

@acowley : I have sent in a pull request towards this issue. I suggest we close this issue and move the discussion to the vinyl-generics repo.