3
votes

I'm trying to make a typeclass for signed numerical types. Here's my code:

{-# LANGUAGE TypeFamilies, FlexibleContexts, UndecidableInstances #-}

data Sign = Negative | Zero | Positive
  deriving (Eq, Ord, Read, Show)

class Signed a where
  sign :: a -> Sign

instance Signed Integer where
  sign = undefined

This compiles, but I'd like to adapt this code to work on any Integral a.

instance (Integral a) => Signed a where
  sign = undefined

At which point it fails to compile.

I've checked Haskell type family instance with type constraints, but that seems to be addressing a different problem from mine. I don't think there's a syntax error, in my code.

1
You'd need to turn on OverlappingInstances, I think. On the other hand, I think putting sign in a class may be a mistake in this case - just define a top-level function sign :: Integral a => a -> Sign.Benjamin Hodgson
Just tried it. Doesn't seem to change. instance Signed Integer still compiles, but instance (Integral a) => ... still fails.Fried Brice
OK, I put your code into a file and GHC told me I needed to turn on FlexibleInstances too. With that, it compiles right away. (GHC's error messages are usually quite specific about which extensions you need to turn on.)Benjamin Hodgson
You're right. It does tell me... Thank you.Fried Brice

1 Answers

2
votes

Attempting to compile your code produces the following error message:

sign.hs:9:26:
    Illegal instance declaration for ‘Signed a’
      (All instance types must be of the form (T a1 ... an)
       where a1 ... an are *distinct type variables*,
       and each type variable appears at most once in the instance head.
       Use FlexibleInstances if you want to disable this.)
    In the instance declaration for ‘Signed a’
Failed, modules loaded: none.

As the compiler points out, you need to turn on FlexibleInstances as well as UndecidableInstances. GHC's error messages are usually quite specific, especially when you've forgotten to turn on a language extension. The following compiles right away:

{-# LANGUAGE UndecidableInstances, FlexibleInstances #-}

data Sign = Negative | Zero | Positive
  deriving (Eq, Ord, Read, Show)

class Signed a where
  sign :: a -> Sign

instance (Integral a) => Signed a where
  sign = undefined

However, I think the Signed class may be a mistake in this example. Defining a (non-overloaded) top-level function is much simpler, doesn't require UndecidableInstances (the need for which is often a design smell), and is more expressive of the meaning of your code: the "things you can get the sign of" are precisely the real numbers.

sign :: Real a => a -> Sign
sign x
    | x == 0 = Zero
    | x < 0 = Negative
    | otherwise = Positive