{-| Module : Data.Migration Description : Type-safe migrations for data Copyright : (c) Sandy Maguire, 2019 James King, 2019 License : MIT Maintainer : james@agentultra.com Stability : experimental To begin migrating your data start with a type family for your record and index it with a natural number @ data family Foo (version :: Nat) newtype MyString = MyString { unMyString :: String } deriving (IsString, Show, Eq) data instance Foo 0 = FooV0 { _fooId :: Int , _fooName :: String } deriving (Generic, Show, Eq) data instance Foo 1 = FooV1 { _fooId :: Int , _fooName :: MyString , _fooHonorific :: String } deriving (Generic, Show, Eq) instance Transform Foo 0 where up v = genericUp v (const "esquire") (const MyString) down v = genericDown v (const unMyString) @ You provide an instance of the Transform class for your type in order to specify how to transform version /n/ to version /n + 1/ and back. Presently only simple record types are supported. More to come in the future. -} module Data.Migration where import Data.Kind import GHC.Generics import GHC.TypeLits import Data.Migration.Internal -- | Implement this class on your type family instance to migrate -- values of your type to the new version and back class Transform (f :: Nat -> Type) (n :: Nat) where up :: f n -> f (n + 1) down :: f (n + 1) -> f n -- | Using this function in your 'up' transformation ensures that you -- only have to provide functions for fields that change or are added. genericUp :: forall n src diff . ( diff ~ FieldDiff (Sort (RepToTree (Rep (src n)))) (Sort (RepToTree (Rep (src (n + 1))))) , GTransform diff (src n) (src (n + 1)) , Generic (src (n + 1)) , GUndefinedFields (Rep (src (n + 1))) ) => src n -> Function diff (src n) (src (n + 1)) genericUp = gTransform @diff @_ @(src (n + 1)) undefinedFields -- | This is the opposite of 'genericUp'. genericDown :: forall n src diff . ( diff ~ FieldDiff (Sort (RepToTree (Rep (src (n + 1))))) (Sort (RepToTree (Rep (src n)))) , GTransform diff (src (n + 1)) (src n) , Generic (src n) , GUndefinedFields (Rep (src n)) ) => src (n + 1) -> Function diff (src (n + 1)) (src n) genericDown = gTransform @diff @_ @(src n) undefinedFields