Closed stevemao closed 10 months ago
Actually, I'd suggest
newtype CreatedAt a
= CreatedAt { unCreatedAt :: a }
and then use CreatedAt UTCTime
and CreatedAt SqlTimestamptz
. There's even a module Data.Profunctor.Product.Newtype
to help with that.
Let me know if that helps or if you'd like more guidance.
Thank, I would like you to write a full example this time.
Here's what I got
newtype CreatedAt a
= CreatedAt { unCreatedAt :: a }
deriving (ToJSON, PSG.FromField)
instance Newtype CreatedAt where
constructor = CreatedAt
field = unCreatedAt
type HCreatedAt
= CreatedAt UTCTime
type PGCreatedAt
= CreatedAt O.PGTimestamptz
now' :: O.Field a
now' = O.Column $ HPQ.FunExpr "now" []
nowCreatedAt :: O.Field PGCreatedAt
nowCreatedAt = now'
pgUTCTime' :: Time.ISO8601 t => t -> O.Field a
pgUTCTime' = IPT.unsafePgFormatTime "timestamptz"
instance O.DefaultFromField PGCreatedAt HCreatedAt where
defaultFromField = O.fromPGSFromField
instance Default O.ToFields HCreatedAt (O.Column PGCreatedAt) where
def = O.toToFields (pgUTCTime' . unCreatedAt)
And this is still very similar to my original code.
Thanks a lot!
Nice! But that's not quite what I would recommend. Instead I would recommend this:
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TemplateHaskell #-}
import Opaleye as O
import Data.Profunctor.Product
import Data.Profunctor.Product.Default
import Data.Profunctor.Product.TH
import Data.Aeson
import Data.Time
newtype CreatedAt a
= CreatedAt { unCreatedAt :: a }
deriving (ToJSON)
$(makeAdaptorAndInstance "pCreatedAt" ''CreatedAt)
type HCreatedAt
= CreatedAt UTCTime
type SqlCreatedAt
= CreatedAt (O.Field O.SqlTimestamptz)
nowCreatedAt :: SqlCreatedAt
nowCreatedAt = CreatedAt O.now
instance Default O.FromFields SqlCreatedAt HCreatedAt where
def = pNewtype def
instance Default O.ToFields HCreatedAt SqlCreatedAt where
def = pNewtype def
In other words, just treat CreatedAt
like any other record type (except you get the Newtype
instance, which makes things even simpler!)
Thanks so much!
It's very clean and we don't need to change in the library
I had to update GHC to the latest version to make this work.
instance Default O.FromFields SqlCreatedAt HCreatedAt
doesn't seem necessary and it's already defined in $(makeAdaptorAndInstance "pCreatedAt" ''CreatedAt)
When defining table fields, you just need to do
entityCreatedAt = pNewtype $ requiredTableField "created_at"
Ah yes, nice!
Pasting the complete simplest version, for posterity:
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TemplateHaskell #-}
import Opaleye as O
import Data.Profunctor.Product.TH
import Data.Aeson
import Data.Time
newtype CreatedAt a
= CreatedAt { unCreatedAt :: a }
deriving (ToJSON)
$(makeAdaptorAndInstance "pCreatedAt" ''CreatedAt)
type HCreatedAt
= CreatedAt UTCTime
type SqlCreatedAt
= CreatedAt (O.Field O.SqlTimestamptz)
nowCreatedAt :: SqlCreatedAt
nowCreatedAt = CreatedAt O.now
I can put createdAt in place of updatedAt and this can cause a bug without compilation errors
createdAt
andupdatedAt
.SqlTimestamptz
Here is what I got to fix the problem:
This way PGCreatedAt can be only mapped from CreatedAt
Is this the correct way to do it? If so, I can submit a PR to add more polymorphic functions such as
pgUTCTime'
andnow'