Following up on this, I have the following typeclasses:
class Monoid m => BuilderS m a where
cstr :: String -> a -> m
class SafeCopy a where
putSafe :: a -> m
Providing instances for BuilderS
:
import qualified Data.Serialize.Builder as B
instance Serialize a => BuilderS B.Builder a where
cstr _ = B.fromByteString . encode
instance BuilderS CustomBuilder Int where
cstr = ...
instance BuilderS CustomBuilder String where
cstr = ...
etc.
I would like to define instances of SafeCopy
like this:
data Person = Person { name :: String, age :: Int }
instance SafeCopy Person where
putSafe p = cstr "name" (name p)
However, in this specific case the compiler can't find an instance of BuilderS m String
. I've tried several things:
- Add all primitive datatypes to the constraints of
putSafe
:putSafe :: (BuilderS m Int, BuilderS m String, ...) => a -> m
. This works, but is not extensible (i.e. what if I want to have aBuilderS m Vector
constraint in the future?) - Add
m
to the type parameters ofSafeCopy
. - Use a custom sum type:
data SumT m = forall a b. (BuilderS m a, BuilderS m b) => a :+: b
and then haveputSafe :: a -> SumT m
.
Still, I'm not providing enough information to the type system so it can defer the decision of which exact instance of BuilderS
to use for later. What am I missing?
You can do something like this:
You'll need to turn on a lot of language extensions.