12

I have to define a type class Truthy which contains a method true converting an instance of the type class to a Bool value.

My type class declaration:

class Truthy a where
    true :: a -> Bool

Next, I have to define instances of this class for various types, including list and numeric types. I have done it for lists and Ints, but is there a way to do it for all numeric types at once?

Based on my Int declaration:

instance Truthy Int where
    true = (/=) 0

I have tried adding type class constraint, but it does not work:

instance (Num a) => (Truthy a) where
    true = (/=) 0::a

If there is a way to do this similar to what I had in mind, or should I just define it for each numeric type separately?

Jason Aller
  • 3,541
  • 28
  • 38
  • 38
penelope
  • 8,251
  • 8
  • 45
  • 87
  • "or should I just define it for each numeric type separately?" - in a word **Yes**. The design goal of type classes is to allow overloading on a per-type basis. With extensions it can be possible to define "overreaching" instances but they are usually a design flaw. – stephen tetley Jan 16 '12 at 19:24
  • possible duplicate of [Declare all instances of a typeclass are in another typeclass without modifying the original class declarations](http://stackoverflow.com/questions/8461029/declare-all-instances-of-a-typeclass-are-in-another-typeclass-without-modifying) – Mikhail Glushenkov Jan 16 '12 at 21:50

1 Answers1

13

This might not help for homework, but you actually can write a declaration like that. You just have to enable -XFlexibleInstances to do so. At least in GHC, you can do this by putting a pragma at the top of your file:

{-# LANGUAGE FlexibleInstances #-}

If you look closely at the error message you got, it said something like "Use -XFlexibleInstances if you want to disable this.".

In this particular case, you would also need to enable UndecidableInstances and OverlappingInstances:

 {-# LANGUAGE FlexibleInstances,  UndecidableInstances, OverlappingInstances #-}

You need FlexibleInstances because standard Haskell does not allow instances in any form where the type variable appears more than once in the head. This is completely fine--I it is one of the most common extensions used (as per this question).

You need UndecidableInstances because your instance declaration could potentially cause the type checker to loop forever. I think using UndecidableInstances prevents this by limiting how deeply it will check when trying to reduce the instance. This is usually--including in this case--fine, but could theoretically make whether a particular program passes the type checks implementation dependent. Still, it should work in your case.

As hammar pointed out, you need to enable OverlappingInstances because the "context" of the instance is ignored when checking whether they overlap. The context is the Num a bit in this case. So the instances--for checking if it overlaps--is read as instance Truthy a... and overlaps with everything. With OverlappingInstances enabled, you just need to have one instance that is the most specific for this to work.

Tikhon Jelvis
  • 67,485
  • 18
  • 177
  • 214
  • I saw that in the error log. It would be great to get an explanation about why I need to enable anything extra in order to get this to compile, or at leas where I can read up on that. Also, is that the only way to do what I want? – penelope Jan 16 '12 at 09:15
  • Basically, the Haskell 98 report specified that class declarations have to be "simple". So *standard* Haskell does not allow the style you tried to use (unless it changed in 2010?). However, the code you wrote is actually reasonable and implementable, so it's supported by GHC; the reason you need the pragma is that it's not in the standard. – Tikhon Jelvis Jan 16 '12 at 09:20
  • For more information than you probably want, you can read about it in the docs [here](http://www.haskell.org/ghc/docs/7.0.1/html/users_guide/type-class-extensions.html#superclass-rules) and [here](http://www.haskell.org/ghc/docs/7.0.1/html/users_guide/other-type-extensions.html#flexible-contexts). – Tikhon Jelvis Jan 16 '12 at 09:21
  • UndecideableInstances just means that the compiler will allow instances that could potentially make the typechecker loop. I think the way it handles it is by limiting the number of recursions it allows when checking the type. [Here](http://www.haskell.org/ghc/docs/7.2.2/html/users_guide/type-class-extensions.html#undecidable-instances) is the documentation for them. – Tikhon Jelvis Jan 16 '12 at 09:24
  • Looking at [this](http://stackoverflow.com/questions/4826630/type-class-problem-concerning-flexibleinstances) question, it seems your best option is to use `FlexibleInstances`. It's a completely reasonable thing to do and I don't think there is any alternative (short of declaring an instance for each type) in standard Haskell. – Tikhon Jelvis Jan 16 '12 at 09:36
  • 3
    You will also need `OverlappingInstances` if you want any instances other than the one for numbers, since it overlaps with everything. This is because the instance head is `a` which matches anything. The `Num` constraint isn't checked until after. – hammar Jan 16 '12 at 09:40
  • @hammar: *Any* instances? I tried adding a trivial instance for `Bool` and it worked just with `FlexibleInstances` and `UndecideableInstances`. Is there something more complicated at play? – Tikhon Jelvis Jan 16 '12 at 09:42
  • Also, are my reasons for the two instances broadly correct? I think they are, but I am not 100% certain. – Tikhon Jelvis Jan 16 '12 at 09:43
  • 2
    @TikhonJelvis: Defining the instances is fine, but have you tried actually using them? – hammar Jan 16 '12 at 09:43
  • Oh, good point. I guess I'm getting spoiled by having most of my errors caught at compile time :) – Tikhon Jelvis Jan 16 '12 at 09:46