Supplying a default implementation for a typeclass based on another class

I'm in the process of writing a distributed filesystem (in haskell, yay), which of course means I'm using Binary for de/serialization. Now, that's fine enough, but for simplicity (and for wireshark), I'd like to be able to have Binary fall back on an instance based on Show/Read for any type that lacks any other Binary instance.. Well, I understand why this would be somewhere between extremely hard and impossible in haskell '98, but I'm not entirely up on all the extensions, so I thought I'd ask - given every extension implemented in ghc 6.10.1, is there any reasonable way to do this?

Now, that's fine enough, but for simplicity (and for wireshark), I'd like to be able to have Binary fall back on an instance based on Show/Read for any type that lacks any other Binary instance..
Well, I understand why this would be somewhere between extremely hard and impossible in haskell '98, but I'm not entirely up on all the extensions, so I thought I'd ask - given every extension implemented in ghc 6.10.1, is there any reasonable way to do this?
I'm not sure what you mean by falling back on Show/Read instances, but this seems like a typical generic programming problem. Recent releases include the EMGM and SYB libraries for generic programming. -- Johan Jeuring

If you were writing your own Binary class you could simply make Show
and Read superclasses of Binary and it would be trivial to have
default implementations based on Show and Read.
-- Lennart
On Sat, Feb 28, 2009 at 10:59 AM, Svein Ove Aas
I'm in the process of writing a distributed filesystem (in haskell, yay), which of course means I'm using Binary for de/serialization.
Now, that's fine enough, but for simplicity (and for wireshark), I'd like to be able to have Binary fall back on an instance based on Show/Read for any type that lacks any other Binary instance..
Well, I understand why this would be somewhere between extremely hard and impossible in haskell '98, but I'm not entirely up on all the extensions, so I thought I'd ask - given every extension implemented in ghc 6.10.1, is there any reasonable way to do this? _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

2009/2/28 Lennart Augustsson
If you were writing your own Binary class you could simply make Show and Read superclasses of Binary and it would be trivial to have default implementations based on Show and Read.
That's my fallback approach, yes, but I don't really want to make Binary a subclass of Show,Read. I'd like to be able to define this for classes in packages I *don't* control, and without *requiring* the fallback to work.. As you say, it isn't exactly a critical issue. I was just wondering if it was possible, really.

On Feb 28, 2009, at 5:59 AM, Svein Ove Aas wrote:
I'm in the process of writing a distributed filesystem (in haskell, yay), which of course means I'm using Binary for de/serialization.
Now, that's fine enough, but for simplicity (and for wireshark), I'd like to be able to have Binary fall back on an instance based on Show/Read for any type that lacks any other Binary instance.
Rather than something funny with extensions, why not write two methods, of type defBinaryPut :: Show a => a -> Put defBinaryGet :: Read a => Get a Then, if somebody tries to use something lacking a Binary instance, they can just define a simple instance with those two methods. And when they want to improve performance, they just write better methods. :-) In fact, these seem useful enough to go into, if not Data.Binary itself, a separate package. After all, proper serialization shouldn't be a barrier to rapid prototyping. Cheers, S.

Hi, you could do something like
instance (Show a,Read a) => Binary a where put = put . show get = fmap read get
But then you will need the following language extensions: FlexibleInstances, OverlappingInstances, UndecidableInstances I don't know how safe this is but it seems to work. Regards, Martin. Svein Ove Aas schrieb:
I'm in the process of writing a distributed filesystem (in haskell, yay), which of course means I'm using Binary for de/serialization.
Now, that's fine enough, but for simplicity (and for wireshark), I'd like to be able to have Binary fall back on an instance based on Show/Read for any type that lacks any other Binary instance..
Well, I understand why this would be somewhere between extremely hard and impossible in haskell '98, but I'm not entirely up on all the extensions, so I thought I'd ask - given every extension implemented in ghc 6.10.1, is there any reasonable way to do this? _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

On Sun, Mar 1, 2009 at 6:42 PM, Martin Huschenbett
Hi,
you could do something like
instance (Show a,Read a) => Binary a where put = put . show get = fmap read get
But then you will need the following language extensions: FlexibleInstances, OverlappingInstances, UndecidableInstances
Well, isn't there a good chance it'll end up picking that instance even when a more specific one is available, then? I think the problem here is that I don't know of any way to inform GHC that any particular instance is "less specific".

Svein Ove Aas wrote:
Martin Huschenbett wrote:
instance (Show a,Read a) => Binary a where put = put . show get = fmap read get But then you will need the following language extensions: FlexibleInstances, OverlappingInstances, UndecidableInstances
Well, isn't there a good chance it'll end up picking that instance even when a more specific one is available, then?
I think the problem here is that I don't know of any way to inform GHC that any particular instance is "less specific".
OverlappingInstances will allow more specific instances to be defined, and will select them when appropriate. Where "specific" has to do with the usual type unification algorithm. This instance is on a type variable, hence is the least specific. An instance on (Maybe a), (Either a b), etc would all be more specific. Instances on (Maybe Int), (Either Bool b), (Either a Double) would be more specific still. Aside: Depending on how you're using this, we can run into problems with, say, (Either Bool Double). Does it fall under (Either Bool b) or under (Either a Double)? By default, OverlappingInstances requires that you manually resolve this diamond issue (i.e. by defining an instance for (Either Bool Double) which is more specific than all other options). The IncoherentInstances option tells the compiler to deal with the ambiguity itself, which means it will pick the most specific instance which does not introduce ambiguity (i.e. the other end of the diamond; in this case, the instance for the fully unspecified type variable). Obviously, incoherence generally does not mean what you want. The UndecidableInstances is because the two elements of the context are not "smaller" than the head (Binary a), and hence the compiler cannot guarantee that progress is being made when it backward-chains to find those instances. To witness why this could be a problem, imagine an instance for Show or Read which took Binary in the context. -- Live well, ~wren
participants (6)
-
Johan Jeuring
-
Lennart Augustsson
-
Martin Huschenbett
-
Sterling Clover
-
Svein Ove Aas
-
wren ng thornton