safecopy-0.10.4.3: src/Data/SafeCopy.hs
{-# LANGUAGE CPP #-}
#ifdef SAFE_HASKELL
{-# LANGUAGE Trustworthy #-}
#endif
-----------------------------------------------------------------------------
-- |
-- Module : Data.SafeCopy
-- Copyright : PublicDomain
--
-- Maintainer : lemmih@gmail.com
-- Portability : non-portable (uses GHC extensions)
--
-- SafeCopy extends the parsing and serialization capabilities of Data.Serialize
-- to include nested version control. Nested version control means that you
-- can change the definition and binary format of a type nested deep within
-- other types without problems.
--
-- = Migration
--
-- Consider this scenario. You want to store your contact list on disk
-- and so write the following code:
--
-- @
--type Name = String
--type Address = String
--data Contacts = Contacts [(Name, Address)]
--instance SafeCopy Contacts where
-- putCopy (Contacts list) = contain $ safePut list
-- getCopy = contain $ Contacts \<$\> safeGet
-- @
--
-- At this point, everything is fine. You get the awesome speed of Data.Serialize
-- together with Haskell's ease of use. However, things quickly take a U-turn for the worse
-- when you realize that you want to keep phone numbers as well as names and
-- addresses. Being the experienced coder that you are, you see that using a 3-tuple
-- isn't very pretty and you'd rather use a record. At first you fear that this
-- change in structure will invalidate all your old data. Those fears are quickly quelled,
-- though, when you remember how nifty SafeCopy is. With renewed enthusiasm,
-- you set out and write the following code:
--
-- @
--type Name = String
--type Address = String
--type Phone = String
--
--{- We rename our old Contacts structure -}
--data Contacts_v0 = Contacts_v0 [(Name, Address)]
--instance SafeCopy Contacts_v0 where
-- putCopy (Contacts_v0 list) = contain $ safePut list
-- getCopy = contain $ Contacts_v0 \<$\> safeGet
--
--data Contact = Contact { name :: Name
-- , address :: Address
-- , phone :: Phone }
--instance SafeCopy Contact where
-- putCopy Contact{..} = contain $ do safePut name; safePut address; safePut phone
-- getCopy = contain $ Contact \<$\> safeGet \<*\> safeGet \<*\> safeGet
--
--data Contacts = Contacts [Contact]
--instance SafeCopy Contacts where
-- version = 2
-- kind = extension
-- putCopy (Contacts contacts) = contain $ safePut contacts
-- getCopy = contain $ Contacts \<$\> safeGet
--
--{- Here the magic happens: -}
--instance Migrate Contacts where
-- type MigrateFrom Contacts = Contacts_v0
-- migrate (Contacts_v0 contacts) = Contacts [ Contact{ name = name
-- , address = address
-- , phone = \"\" }
-- | (name, address) <- contacts ]
-- @
--
-- With this, you reflect on your code and you are happy. You feel confident in the safety of
-- your data and you know you can remove @Contacts_v0@ once you no longer wish to support
-- that legacy format.
--
-- = Retiring Migrations
--
-- There may come a time when you have to remove @Contacts_v0@.
-- Perhaps it uses types you want to remove from your build to
-- decrease its size. Perhaps it has constraints such as @Enum@ which
-- are imposed on your new @Contacts@ type via the @SafeCopy@
-- instance.
--
-- In any case, if you are using @safecopy@ incombination with
-- @acid-state@, some care must be taken when removing @Contacts_v0@.
-- The following steps must be taken to add a new type and remove the old:
--
-- 1. Add the migration as described above.
-- 2. Run the server with the new migration on /all important data/. This
-- will cause the type to be modified in the running program.
-- 3. /Restart/ the server with the new migration on /all important data/. This
-- causes checkpoints to be written that only contain the new type.
-- 4. Remove the old type from your source code, changing the @kind@ of
-- the new type from @extension@ to @base@. Build and deploy.
--
-- If you omit any of these steps it is a certainty that you will
-- proceed happily with your development thinking all is grand, and
-- then when you go to deploy your live system the migration will fail.
module Data.SafeCopy
(
safeGet
, safePut
, SafeCopy(version, kind, getCopy, putCopy, objectProfile, errorTypeName)
, SafeCopy'
, Profile(..)
, Prim(..)
, Migrate(..)
, Reverse(..)
, Kind
, extension
, extended_extension
, extended_base
, base
, Contained
, contain
, Version
-- * Template haskell functions
, deriveSafeCopy
, deriveSafeCopyIndexedType
, deriveSafeCopySimple
, deriveSafeCopySimpleIndexedType
, deriveSafeCopyHappstackData
, deriveSafeCopyHappstackDataIndexedType
-- * Rarely used functions
, getSafeGet
, getSafePut
, primitive
) where
import Data.SafeCopy.Instances ()
import Data.SafeCopy.SafeCopy
import Data.SafeCopy.Derive