Practise of hiding contsructors and serialization

While working on extending scion I got some trouble and wonder which is the best way to do this? I have to pass a compilation result from a scion process instance to the scion daemon process. Of course I'd just like to use a simple Read Show for that. However I can't because two datatypes are NominalDiffTime and Bag ErrMsg: newtype NominalDiffTime = MkNominalDiffTime Pico deriving (Eq,Ord) data ErrMsg = ErrMsg { errMsgSpans :: [SrcSpan], errMsgContext :: PrintUnqualified, errMsgShortDoc :: Message, errMsgExtraInfo :: Message } Both are fine and both don't export all contstructors so you can't just let ghc derive instances for it? So the only way is to duplicate those datatypes in order to derive some Show, Read instances? On the one hand I do understand that you don't want users to access constructors directly. On the other hand it would be desirable to derive Read, Show instances here. Is there a way to get all? a) hiding constructors so that you don't have to expose implementation details to the user. b) still allow deriving serialization instances? Is there a better way than using toRational to get a type which can be serialzied quickly? I don't like Rational because the unit information 1e-12[s] is no longer included in the type.. Sincerly Marc Weber

On Sun, Feb 22, 2009 at 12:55 PM, Marc Weber
While working on extending scion I got some trouble and wonder which is the best way to do this?
I have to pass a compilation result from a scion process instance to the scion daemon process. Of course I'd just like to use a simple Read Show for that. However I can't because two datatypes are NominalDiffTime and Bag ErrMsg:
newtype NominalDiffTime = MkNominalDiffTime Pico deriving (Eq,Ord) data ErrMsg = ErrMsg { errMsgSpans :: [SrcSpan], errMsgContext :: PrintUnqualified, errMsgShortDoc :: Message, errMsgExtraInfo :: Message }
Both are fine and both don't export all contstructors so you can't just let ghc derive instances for it?
So the only way is to duplicate those datatypes in order to derive some Show, Read instances?
On the one hand I do understand that you don't want users to access constructors directly. On the other hand it would be desirable to derive Read, Show instances here.
Is there a way to get all? a) hiding constructors so that you don't have to expose implementation details to the user. b) still allow deriving serialization instances?
I usually use Data.Binary for serialization. There is a cultural standard not to do anything with Binary instances other than to pass the result of encode to decode. They are opaque blobs of bits, perfect for serialization. (I think the only righter way would be to pass the information over a typed channel that respected privacy) Luke

I usually use Data.Binary for serialization. Sure. But you can't derive Data.Binary easily for NominalDiffTime. That's the point. You can only do so by using toReal or by adding data MyOwnPico = MyOwnPico only to create serialization instances. I don't like this kind of code duplication..
Marc

On Sun, Feb 22, 2009 at 5:23 PM, Marc Weber
I usually use Data.Binary for serialization. Sure. But you can't derive Data.Binary easily for NominalDiffTime. That's the point. You can only do so by using toReal or by adding data MyOwnPico = MyOwnPico only to create serialization instances. I don't like this kind of code duplication..
Marc
One advantage of the current system is that is preserves data abstraction. For example, right now NominalDiffTime is declared
newtype NominalDiffTime = MkNominalDiffTime Pico
so it would be possible for GHC or another automated tool to derive an instance of Read, Show, Binary, whatever for it. However, what if, in the future, the maintainer of the Time library wanted to change this to
newtype NominalDiffTime = MkNominalDiffTime Double
? Now your derived instance wouldn't be the same, so your serialized data would not be compatible even between the same version of your library even if the version of your library stayed the same because the time library version had same. Even worse, what if the Time library maintainer changed it to
newtype NominalDiffTime = MkNominalDiffTime (Int -> Pico)
? Now you can't make a derived instance at all! There's no really good reason why they would make these changes; it seems reasonable that the Time library will stay in much the same form for the forseeable future. However, the whole point of abstract datatypes is that the maintainer can change the internals without disrupting dependent packages, as long as the accessor functions has the same semantics. In order for this to work, you can't have any external code - even derived instances - relying on the internal structure of abstract datatypes. Alex
participants (3)
-
Alexander Dunlap
-
Luke Palmer
-
Marc Weber