Haskell无法从Context错误推断出......

时间:2012-02-19 21:14:49

标签: haskell

我有类型类,我希望所有类型都有一些常见的行为。我的问题在以下代码中解释:

class A a
class B b

class X x where
    method :: (A a, B b) =>  x -> a -> b

data T = L | M | N
data U = P | Q | R

instance A T
instance B U

data Y = ZZZ

instance X Y where
    method _ L = P
    method _ M = Q
    method _ N = R

当我加载此模块时,出现以下错误:

example.hs:19:14:
    Could not deduce (a ~ T)
    from the context (A a, B b)
      bound by the type signature for method :: (A a, B b) => Y -> a -> b
      at example.hs:(17,5)-(19,18)
      `a' is a rigid type variable bound by
          the type signature for method :: (A a, B b) => Y -> a -> b
          at example.hs:17:5
    In the pattern: N
    In an equation for `method': method _ N = R
    In the instance declaration for `X Y'

example.hs:19:18:
    Could not deduce (b ~ U)
    from the context (A a, B b)
      bound by the type signature for method :: (A a, B b) => Y -> a -> b
      at example.hs:(17,5)-(19,18)
      `b' is a rigid type variable bound by
          the type signature for method :: (A a, B b) => Y -> a -> b
          at example.hs:17:5
    In the expression: R
    In an equation for `method': method _ N = R
    In the instance declaration for `X Y'
Failed, modules loaded: none.

在这种情况下我无所适从。即使T和U是A和B的实例,我也会收到此错误。如果我无法从method返回刚性类型值,我该如何编写此部分?

2 个答案:

答案 0 :(得分:11)

签名method :: (A a, B b) => x -> a -> b承诺method适用于每个类型(a, b),其a个实例为Ab B的实例,但您将其定义为仅适用于两种特定类型。

这与Java等中的接口根本不同,其中被调用者选择使用哪种类型,调用者唯一知道的是接口X的实现。在Haskell中,给定这样的签名,调用者决定使用哪些类型(这里,作为第二个参数传递什么类型以及应返回什么类型)并且被调用者必须能够提供所需的功能(只要需要) types是所需类的实例。)

如果没有类AB中的任何方法来分别分析该类实例的构造值,除method之外不能实现undefined(不同程度)由于seq)可能导致未定义,因此您必须告诉全世界您实际上正在使用TU

另一种方法是使X成为多参数类型

{-# LANGUAGE MultiParamTypeClasses #-}

class (A a, B b) => X x a b where
    method :: x -> a -> b

但是,这可能需要功能依赖来解析实例。另一种方法是使用相关类型,

{-# LANGUAGE TypeFamilies #-}

class X x where
    type AType x
    type BType x
    method :: x -> AType x -> BType x

instance X Y where
    type AType Y = T
    type BType Y = U
    method ...

答案 1 :(得分:3)

很难想出一种方法来拯救你的榜样(由于Daniel Fischer提到的原因)。如果你有两个完全不相关的类型A和B,你应该如何建立一般连接?所以你可以把它们放在一起,但我怀疑这是你想要的:

{-# LANGUAGE MultiParamTypeClasses #-}

class A2B a b where
  convert :: a -> b

data T = L | M | N
data U = P | Q | R

instance A2B T U where
  convert L = P
  convert M = Q
  convert N = R  

data Y = ZZZ

class X x where
    method :: (A2B a b) =>  x -> a -> b

instance X Y where
    method _ t = convert t

这种设计也不是很稳定:当你希望在A2B T V旁边有A2B T U这样的实例时,你会遇到麻烦。