# safecopy

# Intro

SafeCopy extends the parsing and serialization capabilities of Data.Serialize to include nested version control. Nested version control means that you can change the defintion and binary format of a type nested deep within other types without problems.

## SafeCopy and acid-state.

The packages safecopy and acid-state go hand-in-hand. You’ll never lose your data with acid-state but any changes in your data schema can easily make it unreadable until those schema changes are reverted. SafeCopy picks up the ball from here and delivers it securely over the goal line.

## Extension and migrations

Let’s consider a simple example where we migrate from one type to another.

```
-- We thought Int would be enough but doesn't seem to be the case.
-- To be safe, let's use Integers instead.
data OldType = OldType Int deriving (Show)
data NewType = NewType Integer deriving (Show)
-- Notice that putCopy/getCopy are pretty much identical in both instances.
instance SafeCopy OldType where
putCopy (OldType n) = contain $ safePut n
getCopy = contain $ OldType <$> safeGet
-- A key feature here is that we don't mention the type of the previous version.
instance SafeCopy NewType where
version = 2
kind = extension
putCopy (NewType n) = contain $ safePut n
getCopy = contain $ NewType <$> safeGet
instance Migrate NewType where
type MigrateFrom NewType = OldType
migrate (OldType n) = NewType (fromIntegral n)
-- note: b should be (Either String b)
-- keeping just b because it is easier to read
safeCoerce :: (SafeCopy a, SafeCopy b) => a -> b
safeCoerce a
= let serialized = runPut (safePut a)
in runGet safeGet serialized
```

The above code allows us to parse old serialization in the following way:

```
> safeCoerce (OldType 1337) :: NewType
NewType 1337
> safeCoerce (NewType 1337) :: NewType
NewType 1337
```

## Chains

Migrations are not limited to a single step. You can build up long chains of migrations and SafeCopy will dutifully do the migrations for you.

```
data X1 = X1 Word8 deriving (Show)
data X2 = X2 Word16 deriving (Show)
data X3 = X3 Word32 deriving (Show)
instance SafeCopy X1 where
putCopy (X1 n) = contain (safePut n); getCopy = contain $ X1 <$> safeGet
instance SafeCopy X2 where
version = 2
kind = extension
putCopy (X1 n) = contain (safePut n); getCopy = contain $ X1 <$> safeGet
instance SafeCopy X2 where
version = 3
kind = extension
putCopy (X1 n) = contain (safePut n); getCopy = contain $ X1 <$> safeGet
instance Migrate X2 where
type MigrateFrom X2 = X1
migrate (X1 n) = X2 (fromIntegral n)
instance Migrate X3 where
type MigrateFrom X3 = X2
migrate (X2 n) = X3 (fromIntegral n)
```

We now have a chain of extensions: X1 -> X2 -> X3.

```
> safeCoerce (X1 42) :: X2
X2 42
> safeCoerce (X1 42) :: X3
X3 42
> safeCoerce (X2 42) :: X3
X3 42
> safeCoerce (X3 42) :: X3
X3 42
```

## Branches

Types can only extend a single type but each type can have multiple extensions. Consider the following code:

```
data OldType = OldType Int deriving (Show)
data LeftBranch = LeftBranch String deriving (Show)
data RightBranch = RightBranch Integer deriving (Show)
instance SafeCopy OldType where
putCopy (OldType n) = contain $ safePut n
getCopy = contain $ OldType <$> safeGet
instance SafeCopy LeftBranch where
version = 2
kind = extension
putCopy (LeftBranch str) = contain $ safePut str
getCopy = contain $ LeftBranch <$> safeGet
-- Notice how both LeftBranch and RightBranch have the same version.
-- This would result in a run-time error if we marked either one as
-- the extension of the other.
instance SafeCopy RightBranch where
version = 2
kind = extension
putCopy (RightBranch n) = contain $ safePut str
getCopy = contain $ RightBranch <$> safeGet
instance Migrate LeftBranch where
type MigrateFrom LeftBranch = OldType
migrate (OldType n) = LeftBranch (show n)
instance Migrate RightBranch where
type MigrateFrom RightBranch = OldType
migrate (OldType n) = RightBranch (fromIntegral n)
```

The following coercions are possible:

```
> safeCoerce (OldType 5) :: LeftBranch
LeftBranch "5"
> safeCoerce (OldType 5) :: RightBranch
RightBranch 5
```