Flexible instances needed?

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?


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 a s 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.


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.

链接地址: http://www.djcxy.com/p/33168.html

上一篇: Haskell中的恶意实例解析

下一篇: 需要灵活的实例?