Question

I want to write a Convertible instance for a Haskell type to its C representation

It looks like this:

instance Convertible Variable (IO (Ptr ())) where

Now GHC complains:

 Illegal instance declaration for `Convertible
                                    Variable (IO (Ptr ()))'
  (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 -XFlexibleInstances if you want to disable this.)
In the instance declaration for `Convertible Variable (IO (Ptr ()))'

I thought Flexible Instances were needed if you had free types in your instance declaration, but this is not the case. I can get it to compile when adding the right pragma, but can anyone explain why I need this?

Was it helpful?

Solution

Yes you need flexible instances. Without it, all of your typeclass instance would have to look like

instance Foo (Maybe a) where
instance Foo (IO a)    where
instance Foo (Either a b) where

If you want to do anything other than TypeConstructor a1 a2 a3 ... (and as have to be type variables) than you need flexible instances. But it's one of the most common language extensions, don't sweat using it.

OTHER TIPS

You can often avoid FlexibleInstances in cases like this. There are two general approaches:

Use an auxiliary class

instance ConvIO a => Convertible Variable (IO a) where
  method = methodIO

class ConvIO p where
  methodIO :: ...
  -- similar to the signature for the Convertible method,
  -- but with Variable and IO baked in

instance ConvIOPtr u => ConvIO (Ptr u) where
   methodIO = methodIOPtr

class ConvIOPtr u where
   methodIOPtr :: ...

instance ConvIOPtr () where ...

This approach works well when you need several instances headed by the same constructor.

Use an equality constraint

Turn on GADTs or TypeFamilies and write

instance a ~ Ptr () => Convertible Variable (IO a) where ...

This approach tends to help a lot with type inference, but only makes sense if you only need one instance headed by IO.

You can mix and match

You can use an auxiliary class to go through IO, and then an equality constraint to go through Ptr.

instance u ~ () => ConvIO (Ptr u) where ...

Or you can use an equality constraint to go through IO and an auxiliary class to go through Ptr:

instance (a ~ Ptr u, ConvIOPtr u) => Convertible Variable (IO a)

Where these won't work

If you need an instance one of whose arguments is a type variable, then you really can't avoid FlexibleInstances at all. You might be able to work around the problem using a newtype, but it's not worth the trouble.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top