重叠实例的麻烦

时间:2016-08-30 14:39:20

标签: haskell

我目前正在一个项目中工作,在那里我为一个类派生了一些实例。由于该类只有一个方法具有相同的定义,除了少数特定情况,我尝试定义一个可重叠的通用实例,然后定义我需要重叠的实例。

这不起作用,因为我收到重叠实例错误。做了一些测试,我们遇到了这个简化的例子,它几乎等同于我原来的问题:

{-# LANGUAGE FlexibleInstances, UndecidableInstances, MultiParamTypeClasses #-}

module Instance where

data Id a = Id a String

data C a = C a

class Bad a b where
  bad :: a -> String

instance {-# OVERLAPPABLE #-} Bad a b where
  bad = \_ -> "Default case"

instance {-# OVERLAPPING #-} Bad (Id a) (C a) where
  bad = \_ -> "Id"

class Good a b where
  good :: a -> String

instance {-# OVERLAPPABLE #-} Good a b where
  good = \_ -> "Default case"

instance {-# OVERLAPPING #-} Good (Id a) b where
  good = \_ -> "Id"

test = let a = Id () "a"
       in putStrLn (good a) >> putStrLn (bad a)

(请注意,除非您评论第二个Bad实例,否则不会编译。)

Class Good没有任何问题(测试输出" Id")。如果我没有删除Bad的第二个实例,我会得到:

Overlapping instances for Bad (Id ()) b0
    arising from a use of ‘bad’
  Matching instances:
    instance [overlappable] Bad a b -- Defined at Instance.hs:12:31
    instance [overlapping] Bad (Id a) (C a)
      -- Defined at Instance.hs:15:30
  (The choice depends on the instantiation of ‘b0’
   To pick the first instance above, use IncoherentInstances
   when compiling the other instance declarations)
  In the first argument of ‘putStrLn’, namely ‘(bad a)’
  In the second argument of ‘(>>)’, namely ‘putStrLn (bad a)’
  In the expression: putStrLn (good a) >> putStrLn (bad a)

我不明白为什么会发生这种情况,当它们之间的唯一区别是第二类参数中的附加限制时。

另外,不是重叠实例的重点是避免重叠错误吗?

此致

1 个答案:

答案 0 :(得分:3)

根据我上面的评论,我认为您的pragma应该有with prep ( from_qtr, from_yr, to_qtr, to_yr ) as ( select to_number(substr(:from_year, 2, 1)), to_number(substr(:from_year, 4 )), to_number(substr(:to_year , 2, 1)), to_number(substr(:to_year , 4 )) from dual ), num ( rn ) as (select rownum from all_tab_columns) select add_months( to_date(to_char(from_yr) || '/' || to_char(3 * from_qtr - 2) || '/01', 'yyyy/mm/dd') - 1, 3 * (rn - 1) ) as qtr_end_date from prep cross join num where rn <= 1 + 4 * (to_yr - from_yr) + (to_qtr - from_qtr) order by qtr_end_date ; 而不是num ( rn ) as select 1 from dual union all select 2 from dual union all select 3 from dual ...... ), 否则你会得到一个与函数签名中含糊不清的AllowAmbiguousTypes不同的错误(至少我在GHC 8.0.1上做过) UndecidableInstances

b允许您为使用它们时不明确的函数编写签名。 相反,歧义检查被移动到呼叫站点。这与bad :: Bad a b => a -> String之类的效果相当不错 指定那些不明确的变量。在这种情况下,AmbiguousTypes总是不明确的,所以我们需要这个pragma移动到 呼叫站点上的错误消息。现在,我和你有同样的信息。

即使TypeApplicationsbad Haskell抱怨的原因在于,取决于OVERLAPPABLE如何实例化(也没有 指定),它将选择OVERLAPPING的两个实例之一。换句话说,您可以希望bBad统一, 就像你做不到的那样,所以哈斯克尔举起双手说:“你还没有告诉我足够的b让我成为 能够选择C a&#34;。

的最具体的实例

另一方面,即使不知道b,Haskell也知道Badb中的哪些实例更多 具体 - 它总是第二个(即使不知道Good a b是什么情况)。

我真的建议您阅读有关overlapping instances的文档 因为它解释了整个算法。

您通常可以使用Good (Id a) b(指定b)或将类型类转换为类型系列来解决这些问题。