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.