Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write an instance for all types in another type class?

Tags:

haskell

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?

like image 661
penelope Avatar asked Jan 16 '12 08:01

penelope


1 Answers

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.

like image 198
Tikhon Jelvis Avatar answered Nov 16 '22 01:11

Tikhon Jelvis