【问题标题】:Haskell type classes and type families (cont'd)Haskell 类型类和类型族(续)
【发布时间】:2011-04-19 07:30:14
【问题描述】:

我需要一些帮助来找出真正让我抓狂的编译器错误...

我有以下类型类:

infixl 7 -->
class Selectable a s b where
  type Res a s b :: *
  (-->) :: (CNum n) => (Reference s a) -> (n,(a->b),(a->b->a)) -> Res a s b

我实例化了两次。第一次就像一个魅力:

instance Selectable a s b where
  type Res a s b = Reference s b
  (-->) (Reference get set) (_,read,write) = 
        (Reference (\s -> 
                      let (v,s') = get s
                      in (read v,s'))
                   (\s -> \x ->
                      let (v,s') = get s
                          v' = write v x
                          (_,s'') = set s' v'
                      in (x,s'')))   

因为类型检查器推断

(-->) :: Reference s a -> (n,a->b,a->b->a) -> Reference s b

并且此签名与 (-->) 的类签名匹配,因为

Res a s b = Reference s b

现在我添加了第二个实例,一切都中断了:

instance (Recursive a, Rec a ~ reca) => Selectable a s (Method reca b c) where
  type Res a s (Method reca b c) = b -> Reference s c
  (-->) (Reference get set) (_,read,write) =
    \(x :: b) -> 
        from_constant( Constant(\(s :: s)->
                          let (v,s') = get s :: (a,s)
                              m = read v
                              ry = m x :: Reference (reca) c
                              (y,v') = getter ry (cons v) :: (c,reca)
                              v'' = elim v'
                              (_,s'') = set s' v''
                              in (y,s''))) :: Reference s c

编译器抱怨

Couldn't match expected type `Res a s (Method reca b c)'
       against inferred type `b -> Reference s c'
The lambda expression `\ (x :: b) -> ...' has one argument,
which does not match its type
In the expression:
    \ (x :: b)
        -> from_constant (Constant (\ (s :: s) -> let ... in ...)) ::
             Reference s c
In the definition of `-->':
    --> (Reference get set) (_, read, write)
          = \ (x :: b)
                -> from_constant (Constant (\ (s :: s) -> ...)) :: Reference s c

仔细阅读编译器告诉我它已经推断出 (-->) 的类型:

(-->) :: Reference s a -> (n,a->(Method reca b c),a->(Method reca b c)->a) -> (b -> Reference s c)

这是正确的,因为

Res a s (Method reca b c) = b -> Reference s c

但是为什么不能匹配两个定义呢?

很抱歉没有提供更简洁和独立的示例,但在这种情况下,我不知道该怎么做...

【问题讨论】:

  • 你能给出一个完整的可运行的例子吗?即Reference 来自哪里?甚至包括{-# LANGUAGE TypeFamilies #-}。恕我直言,可以帮助其他人帮助您。
  • 它有点大...这只是一个更大的项目的一小部分,我不可能将其全部发布:(

标签: haskell typeclass


【解决方案1】:

当你写作时

instance Selectable a s b where

您是说任何类型的组合都是 Selectable 的实例。这没有为其他实例留下空间。

当然,某些可疑的编译器扩展会允许您编写更多(必然是冲突的)实例,但您肯定会遇到麻烦。

您能否使您的第一个实例更加具体,使其不再与您尝试编写的其他实例冲突?

遇到此类问题通常表明类型类不是解决方案。如果您只编写两个实例,为什么不放弃重载而只编写两个特定的函数——一个用于每个用例?

【讨论】:

  • 好吧,我现在确实有两个函数,但是由于它们具有非常相似的语义,重载感觉像是正确的方法......我需要指定类似“如果没有与第二个实例发生特定匹配然后去第一个”,但我开始担心这可能不可行:(
  • Giuseppe,this page on the wiki 中描述的想法对你有用吗?好像很能描述你的情况!
  • 他是否有可能将不太通用的定义放在首位,然后将另一个作为“后备”案例?
  • 我不得不同意 Martijn 的观点,即两个 (-->) 函数看起来一点也不相似,也不应该有相同的名称——我知道第一个是什么,但还没有关于第二个的最模糊的想法!
【解决方案2】:

就像我在其他地方所说的那样,鉴于如此少的上下文,我不知道在第二种情况下发生了什么。但是,也许您可​​以通过这样做来使您的实例不重叠:

class Selectable a s b r where
    (-->) :: (CNum n) => (Reference s a) -> (n,(a->b),(a->b->a)) -> r

instance Selectable a s b (Reference s b) where ...
instance (Recursive a, Rec a ~ reca) => Selectable a s (Method reca b c) (b -> Reference s c) where ...

不过,这可能会导致您在调用站点出现问题,并且使用具有不同名称的两个函数可能要好得多。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-01-10
    • 1970-01-01
    • 1970-01-01
    • 2022-08-12
    • 1970-01-01
    • 2014-10-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多