haskell safecopy example - haskell

(src http://hackage.haskell.org/packages/archive/safecopy/0.6.1/doc/html/Data-SafeCopy.html)
If you rename Contacts data type into data Contacts_v0
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
How is Contacts_v0 suppose to be assigned to old existing data?
type Name = String
type Address = String
type Phone = String
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
instance Migrate Contacts where
type MigrateFrom Contacts = Contacts_v0
migrate (Contacts_v0 contacts) = Contacts [ Contact{ name = name
, address = address
, phone = "" }
| (name, address) <- contacts ]
From the above library documentation I am trying to do this.
{-# LANGUAGE RecordWildCards, TypeFamilies #-}
import Control.Applicative
import Data.SafeCopy
type Name = String
type Address = String
type Phone = String
data Contacts = Contacts [(Name, Address)] deriving (Show)
instance SafeCopy Contacts where
putCopy (Contacts list) = contain $ safePut list
getCopy = contain $ Contacts <$> safeGet
data Contacts_v0 = Contacts_v0 [(Name, Address)] deriving (Show)
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 } deriving (Show)
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
instance Migrate Contacts where
type MigrateFrom Contacts = Contacts_v0
migrate (Contacts_v0 contacts) = Contacts [ Contact{ name = name, address = address, phone = "" }
| (name, address) <- contacts ]
-}
main :: IO ()
main = do
let test = Contacts [("gert","home")]
print test
--let testNew = how do you migrate test using migrate?
--print testNew
Note that it would make more sense to me if they renamed the new one to Contacts_v2 instead of renaming the old one.
Maybe I should rephrase the question, when is safecopy useful?

{-# LANGUAGE RecordWildCards, TypeFamilies#-}
import Control.Applicative
import Data.SafeCopy
import Data.Binary
import Data.Serialize.Get
import Data.Serialize.Put
type Name = String
type Address = String
type Phone = String
data Contact = Contact { name :: Name, address :: Address, phone :: Phone } deriving (Show)
instance Binary Contact where
put Contact{..} = do put name; put address; put phone
get = do name <- get; address <- get; phone <- get; return Contact{..}
instance SafeCopy Contact where
putCopy Contact{..} = contain $ do safePut name; safePut address; safePut phone
getCopy = contain $ Contact <$> safeGet <*> safeGet <*> safeGet
data Contacts = Contacts [Contact] deriving (Show)
instance Binary Contacts where
put (Contacts set) = put set
get = fmap Contacts get
instance SafeCopy Contacts where
version = 2
kind = extension
putCopy (Contacts contacts) = contain $ safePut contacts
getCopy = contain $ Contacts <$> safeGet
instance Migrate Contacts where
type MigrateFrom Contacts = Contacts_v0
migrate (Contacts_v0 contacts) = Contacts[Contact{name=name,address=address,phone=""}|(name,address)<-contacts]
data Contacts_v0 = Contacts_v0 [(Name, Address)] deriving (Show)
instance Binary Contacts_v0 where
put (Contacts_v0 set) = put set
get = fmap Contacts_v0 get
instance SafeCopy Contacts_v0 where
putCopy (Contacts_v0 list) = contain $ safePut list
getCopy = contain $ Contacts_v0 <$> safeGet
main :: IO ()
main = do
--
-- instance Binary
--
let c' = Contacts[Contact{name="gert",address="home",phone="test"},Contact{name="gert2",address="home2",phone="test2"}]
let e' = encode c'
print e'
let d' = decode e'
print (d':: Contacts)
let c = Contacts_v0 [("gert_v0","home_v0"),("gert2_v0","home2_v0")]
let e = encode c
print e
let d = decode e
print (d:: Contacts_v0)
--can not do print (d:: Contacts) meaning you are screwed
--
-- instance SafeCopy
--
let c'' = Contacts_v0 [("gert_v0","home_v0"),("gert2_v0","home2_v0")]
let e'' = runPut (safePut c'')
print e''
let d'' = runGet safeGet e''
case d'' of
Left _ -> print "error"
Right d'' -> print (d'':: Contacts)
--can do print (d:: Contacts) or print (d:: Contacts_v0) meaning you are safed

Related

Force keyword arguments in Haskell record init

Can I force the use of keywords only when initializing a record in Haskell?
data Person
= Person
{
name :: String,
idnum :: String
}
deriving( Show )
main = putStrLn $ show $ Person "oren" "9200"
-- I want to only allow such initializations:
-- main = putStrLn $ show $ Person { name = "moish", idnum = "7400" }
(This is especially useful when two fields have the same type)
As far as I know, no.
Consider maybe the following solution?
newtype Name = Name String
newtype Idnum = Idnum String
data Person = Person { name :: Name, idnum :: Idnum }
Another possibility, worse in my opinion, is this:
module A (Person, name, idnum) where
data Person = Person
{ _name :: String
, _idnum :: String
}
name :: String -> (Person -> Person)
name n p = p { _name = n }
idnum :: String -> (Person -> Person)
idnum n p = p { _idnum = n }
emptyPerson :: Person
emptyPerson = Person "" ""
# in another module
module B
import A (Person, name, idnum)
myPerson = name "myname" . idnum "myidnum" $ emptyPerson
In this case there's no guarantee that both name and idnum get a value.
The fact that Person can always be used as a 'plain function' may turn out to be very useful. If you have, for instance, getName :: IO String and getIdnum :: IO String, then combining these to form a getPerson :: IO Person is concise: getPerson = Person <$> getName <*> getIdnum. This is only possible because we don't use record syntax here!

IO action is repeated after changin its internal value

I try printing a value (of the showable Person type) and then changing the return type from IO () to IO Person.
import qualified Data.Text as T
data Person = Person
{ firstName :: T.Text
, lastName :: T.Text
} deriving Show
writePerson :: Person -> IO Person
writePerson p = const p <$> print p
Expected Result:
Person {firstName = "Maria", lastName = "do Rosario"}
Actual Result:
Person {firstName = "Maria", lastName = "do Rosario"}
Person {firstName = "Maria", lastName = "do Rosario"}
You are running this in ghci. The first line is the output of the call to print. The second line is the interpreter showing the return value of the call to writePerson. They are identical because you pass p as the argument to both const and print.

Use Template Haskell to generate instance recursively

In GenericPretty, there is an Out class with a default implementation by using GHC.Generic magic.
As you can see that I defined Person data type, and if I want to implement Out class I have to write 3 times manually since Person used Address and Names data types which should be also the instances of Out class.
I want to generate the instance declaration automatically with Template Haskell. The procedure seems simple.
1, Generate instance A for Person and seek the types which are used to define Person.
2, If the type used to define Person is not an instance A, generate it recursively.
However, gen function will not work. The code generation will not stop, I am not sure why. it could be the problem with mapM if you comment it out, the last line in gen will work.
{-# LANGUAGE CPP, TemplateHaskell,StandaloneDeriving, DeriveGeneric, DeriveDataTypeable #-}
module DerivingTopDown where
import Language.Haskell.TH
import GHC.Generics
import Data.Data
import Data.Proxy
import Control.Monad
import Text.PrettyPrint.GenericPretty
import Data.List
import Debug.Trace
import Control.Monad.State
import Control.Monad.Trans
data Person = Person Names Address
| Student Names Address
deriving (Show, Generic, Eq, Ord , Data,Typeable)
data Names = Names String
deriving (Show, Generic, Eq, Ord, Data, Typeable)
data Address = Address String
deriving (Show, Generic, Eq, Ord, Typeable, Data)
{-
data T a b = C1 a | C2 b
instance (Out a , Out b) => Out (T a b)
([],[NormalC Main.Person [(NotStrict,ConT Main.Names),(NotStrict,ConT Main.Address)],
NormalC Main.Student [(NotStrict,ConT Main.Names),(NotStrict,ConT Main.Address)]])
-}
-- instance Out Address
-- instance Out Names
-- instance Out Person
--- class name -> type name, use a stateT to store a dictionary
gen :: Name -> Name -> StateT [Name] Q [Dec]
gen cla typ = do
(tys, cons) <- lift (getTyVarCons typ)
let typeNames = map tvbName tys
let instanceType = foldl' appT (conT typ) $ map varT typeNames
let context = applyContext cla typeNames
let decltyps = (conT cla `appT` instanceType)
isIns <- lift (typ `isInstanceOf` cla)
table <- get
if isIns || elem typ table -- if it is already the instnace or we have generated it return []
then return []
else do
dec <- lift $ fmap (:[]) $ instanceD context decltyps []
modify (typ:) -- add the generated type to dictionary
let names = concatMap getSubType cons
xs <- mapM (\n -> gen cla n) names
return $ concat xs ++ dec
--return dec -- works fine if do not generate recursively by using mapM
f = (fmap fst ((runStateT $ gen ''Out ''Person) []))
getSubType :: Con -> [Name]
getSubType (NormalC n sts) = map type1 (map snd sts)
type1 :: Type -> Name
type1 (ConT n) = n
tvbName :: TyVarBndr -> Name
tvbName (PlainTV name ) = name
tvbName (KindedTV name _) = name
applyContext :: Name -> [Name] -> Q [Pred]
applyContext con typeNames = return (map apply typeNames)
where apply t = ClassP con [VarT t]
isInstanceOf :: Name -> Name -> Q Bool
isInstanceOf ty inst = do
t1 <- conT (ty)
isInstance inst [t1]
getTyVarCons :: Name -> Q ([TyVarBndr], [Con])
getTyVarCons name = do
info <- reify name
case info of
TyConI dec ->
case dec of
DataD _ _ tvbs cons _ -> return (tvbs,cons)
NewtypeD _ _ tvbs con _ -> return (tvbs,[con])
-- pp = $(stringE . show =<< getCons ''Person)
pp1 name = stringE.show =<< name
isi name = do
t1 <- [t| $name |]
isInstance ''Out [t1]
You have some incomplete function definitions (e.g. type1, tvbName, getTyVarCons) and I am running into that.
I inserted a trace statement in DerivingTopDown.hs at the entry to gen:
import Debug.Trace
...
gen cla typ = trace ("=== typ: " ++ show typ) $ do
...
and then loaded this file into ghci:
{-# LANGUAGE TemplateHaskell #-}
import DerivingTopDown
f
and got the following output:
=== typ: DerivingTopDown.Person
=== typ: DerivingTopDown.Names
=== typ: GHC.Base.String
th.hs:1:1:
Exception when trying to run compile-time code:
DerivingTopDown.hs:(80,17)-(82,68): Non-exhaustive patterns in case
Code: f
Failed, modules loaded: DerivingTopDown.
So it recursed down to GHC.Base.String and then failed in getTyVarCons because the dec for this type is:
dec = TySynD GHC.Base.String [] (AppT ListT (ConT GHC.Types.Char))
which isn't handled by the inner case statement in getTyVarCons.

haskell Data.Binary example

I am trying to serialize a Contacts type but I am stuck at defining put and get?
import Control.Monad
import Data.Binary
type Name = String
type Address = String
data Contacts = Contacts [(Name, Address)] deriving (Show)
instance Binary Contacts where
put (Contacts [(n,a)]) = do ...
get = do ...
main :: IO ()
main = do
let c = Contacts [("gert","home")]
let e = encode c
let d = decode e
print d
Yes, you are stuck defining put and get. Does that answer your question?
type Name = String
type Address = String
data Contacts = Contacts [(Name, Address)] deriving (Show)
instance Binary Contacts
put (Contacts [(n,a)]) = do ...
get = do ...
Since there are already instances:
instance (Binary a) => Binary [a]
instance (Binary a, Binary b) => Binary (a,b)
instance Binary Char
You should just be able to trivially lift the underlying put and get routines:
instance Binary Contacts where
put (Contacts set) = put set
get = fmap Contacts get
So when you put contacts you just tell it to put the list of pairs of strings. When you want to deserialize the contacts you just get the underlying list and use the Contacts constructor.
Adding more simple examples to prevent other noobs from suffering like me :)
{-# LANGUAGE RecordWildCards #-}
import Data.Binary
type Name = String
type Address = String
type Phone = String
data Contacts = Contacts [(Name, Address)] deriving (Show)
instance Binary Contacts where
put (Contacts set) = put set
get = fmap Contacts get
data Contact = Contact { name :: Name, address :: Address, phone :: Phone } deriving (Show)
instance Binary Contact where
put Contact{..} = do put name; put address; put phone
get = do name <- get; address <- get; phone <- get; return Contact{..}
main :: IO ()
main = do
let c = Contacts [("gert","home"),("gert2","home2")]
let e = encode c
print e
let d = decode e
print (d:: Contacts)
let c' = Contact{name="gert",address="home",phone="test"}
let e' = encode c'
print e'
let d' = decode e'
print (d':: Contact)

How can I use read on a string that is not double quoted?

I'm reading values from in from a console using readLn.
I'd like to write a function:
requestValue :: String -> IO a
requestValue s = do
putStrLn $ "Please enter a new value for " ++ s
readLn
I'd then be able to do, for example,
changeAge :: Person -> IO Person
changeAge p = do
age' <- requestValue "age"
return $ p { age = age'}
changeName :: Person -> IO Person
changeName p = do
name' <- requestValue "name"
return $ p { name = name'}
The problem I have is that the read instance of String seems to require the string to be in quotes. I don't want to have to enter "Fred" in the console to change name when I really only want to type in Fred.
Is there an easy way to do this that keeps requestValue polymorphic?
Since you want to add your own custom read behavior for user names, the way to do that is to actually write a new instance for readings names. To do that we can create a new type for names:
import Control.Arrow (first)
newtype Name = Name { unName :: String }
deriving (Eq, Ord, Show)
and write a custom read for it:
instance Read Name where
readsPrec n = map (first Name) . readsPrec n . quote
where quote s = '"' : s ++ ['"']
this is the same as the read instance for strings, but we first quote the string, after reading it in.
Now you can modify your Person type to use Name instead of String:
data Person = Person { age :: Int
, name :: Name } deriving Show
and we're in business:
*Main> changeName (Person 31 (Name "dons"))
Please enter a new value for name
Don
Person {age = 31, name = Name {unName = "Don"}}
You want getLine, not readLn.

Resources