Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell inheriting type classes

Suppose I have the following class:

class P a where
  nameOf :: a -> String

I would like to declare that all instances of this class are automatically instances of Show. My first attempt would be the following:

instance P a => Show a where
  show = nameOf

My first attempt to go this way yesterday resulted in a rabbit warren of language extensions: I was first told to switch on flexible instances, then undecidable instances, then overlapping instances, and finally getting an error about overlapping instance declarations. I gave up and returned to repeating the code. However, this fundamentally seems like a very simple demand, and one that should be easily satisfied.

So, two questions:

  1. Is there a trivially easy way to do this that I've just missed?
  2. Why do I get an overlapping instances problem? I can see why I might need UndecidableInstances, since I seem to be violating the Paterson condition, but there are no overlapping instances around here: there are no instances of P, even. Why does the typechecker believe there are multiple instances for Show Double (as seems to be the case in this toy example)?
like image 756
Impredicative Avatar asked Apr 04 '13 09:04

Impredicative


1 Answers

You get the overlapping instances error because some of your instances of P may have other instances of Show and then the compiler won't be able to decide which ones to use. If you have an instance of P for Double, then there you go, you get two instances of Show for Double: yours general one and the one already declared in Haskell's base library. How this error is triggered is correctly stated by @augustss in the comments to your question. For more info see the specs.

As you already know, there is no way to achieve what you're trying without the UndecidableInstances. When you enable that flag you must understand that you're taking over the compiler's responsibility to ensure that there won't arise any conflicting instances. This means that, of course, there mustn't be any other instances of Show produced in your library. This also means that your library won't export the P class, which will erase the possibility of users of the library declaring the conflicting instances.

If your case somehow conflicts with the said above, it's a reliable sign of that there must be something wrong with it. And in fact there is...


What you're trying to achieve is incorrect above all. You are missing several important points about the Show typeclass, distinguishing it from constructs like a toString method of popular OO languages:

  1. From Show's haddock:

    The result of show is a syntactically correct Haskell expression containing only constants, given the fixity declarations in force at the point where the type is declared. It contains only the constructor names defined in the data type, parentheses, and spaces. When labelled constructor fields are used, braces, commas, field names, and equal signs are also used.

    In other words, declaring an instance of Show, which does not produce a valid Haskell expression, is incorrect per se.

  2. Given the above it just doesn't make sense to declare a custom instance of Show when the type allows to simply derive it.

  3. When a type does not allow to derive it (e.g., GADT), generally you'll still have to stick to type-specific instances to produce correct results.

So, if you need a custom representation function, you shouldn't use Show for that. Just declare a custom class, e.g.:

class Repr a where
  repr :: a -> String

and approach the instances declaration responsibly.

like image 172
Nikita Volkov Avatar answered Oct 13 '22 16:10

Nikita Volkov