需要灵活的实例?

时间:2013-11-22 13:20:27

标签: haskell typeclass

我想将Haskell类型的Convertible实例写入其C表示

看起来像这样:

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

现在GHC抱怨道:

 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 ()))'

如果你的实例声明中有自由类型,我认为需要灵活实例,但事实并非如此。我可以在添加正确的pragma时进行编译,但任何人都可以解释为什么我需要它吗?

2 个答案:

答案 0 :(得分:13)

是的,您需要灵活的实例。没有它,你的所有类型类实例都必须看起来像

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

如果你想做除TypeConstructor a1 a2 a3 ...以外的任何事情(并且a必须是类型变量),那么你需要灵活的实例。但它是最常见的语言扩展之一,不要冒汗使用它。

答案 1 :(得分:2)

在这种情况下,您经常可以避免FlexibleInstances。有两种通用方法:

使用辅助类

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

当您需要多个以相同构造函数为首的实例时,此方法很有效。

使用等式约束

开启GADTsTypeFamilies并写入

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

这种方法对类型推断有很大帮助,但只有在需要一个以IO为首的实例时才有意义。

您可以混合搭配

您可以使用辅助类来浏览IO,然后使用等式约束来浏览Ptr

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

或者您可以使用等式约束来通过IO和辅助类来完成Ptr

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

这些在哪里工作

如果你需要一个实例,其中一个参数是一个类型变量,那么你根本就不能避免FlexibleInstances。您可以使用newtype解决问题,但这不值得。