1

I have a really simple use-case where I have a basic type but for the purposes of my various record types in my data model, I want each record to have wrapped version of this type:

type UID = Integer

-- specialised version
newtype MyRecordID = MyRecordID UID

-- constructor which can take any integral type because I get 
-- Int32 from the database and it's a lot of annoying
-- boilerplate each time
myRecordID :: Integral a => a -> MyRecordID
myRecordID = MyRecordID . fromInteger

There are a few records like this, and instead of writing these constructors by hand each time I thought this might be an ideal way to use TH.

From this answer I managed to make the newtype declaration (although it really bothers me that there is a lot I don't understand in this):

wrapUID :: String -> Q [Dec]
wrapUID n = (:[]) <$> dataD (cxt []) 
                            name
                            []
                            Nothing
                            [normalC name 
                              [bangType (bang noSourceUnpackedness noSourceStrictness)
                              [t| UID |]]]
                            []
  where name = mkName n

I don't know how to go about then creating the "constructor" declaration.

GTF
  • 8,031
  • 5
  • 36
  • 59

1 Answers1

2

If you are content to reuse an existing solution rather than writing your own you can use product-profunctors.

{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}

import Data.Profunctor.Product.TH
import Data.Profunctor.Product.Newtype

type UID = Integer

newtype MyRecordID' a = MyRecordID a
type MyRecordID = MyRecordID' UID

$(makeAdaptorAndInstanceInferrable "pMyRecordID" ''MyRecordID')

myAnything :: (Newtype t, Integral a) => a -> t a
myAnything = constructor . fromIntegral
Tom Ellis
  • 9,224
  • 1
  • 29
  • 54
  • Thanks, although I'm really using this example to learn how to do metaprogramming in Haskell through TH, so if you're able to show how to achieve the above in TH directly that would be great. – GTF Oct 10 '21 at 18:22
  • I'm not sure exactly what you're trying to do, but the TH source code for the above is at https://github.com/tomjaguarpaw/product-profunctors/blob/master/Data/Profunctor/Product/Internal/TH.hs – Tom Ellis Oct 10 '21 at 18:27
  • I'm just trying to generate the code in the first block (the newtype declaration and associated constructor which takes `Integral a`) using something as neat as `$(wrapUID "MyRecordID")`. – GTF Oct 10 '21 at 20:01