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.
上一篇: Haskell中的恶意实例解析
下一篇: 需要灵活的实例?