GHC 7.7中引入的自由覆盖条件在GHC 7.6中有效

这个想法

我正在编写一个编译为Haskell的DSL。

这种语言的用户可以定义自己的不可变数据结构和相关功能。 通过关联函数,我指的是一个属于数据结构的函数。 例如,用户可以编写(在“pythonic”伪代码中):

data Vector a:
  x,y,z :: a
  def method1(self, x):
      return x

(相当于下面的代码,但是也显示了相关函数beheva就像开放世界假设的类型类):

data Vector a:
  x,y,z :: a
def Vector.method1(self, x):
  return x

在本例中, method1是一个与Vector数据类型相关的函数,可以像v.testid(5) (其中vVector数据类型的实例)一样使用。

我正在将这样的代码翻译成Haskell代码,但是我正面临一个问题,我试图解决很长时间。

问题

我试图将代码从GHC 7.6移到GHC 7.7(这是7.8版本的预发行版本)(新版本可以从源代码编译)。 代码在GHC 7.6下完美工作,但不在GHC 7.7下。 我想问你如何解决它,使其在新版本的编译器中工作?

示例代码

让我们看看生成的(通过我的编译器)Haskell代码的简化版本:

{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE FunctionalDependencies #-}

import Data.Tuple.OneTuple

------------------------------
-- data types
------------------------------
data Vector a = Vector {x :: a, y :: a, z :: a} deriving (Show)
-- the Vector_testid is used as wrapper over a function "testid". 
newtype Vector_testid a = Vector_testid a

------------------------------
-- sample function, which is associated to data type Vector
------------------------------
testid (v :: Vector a) x = x

------------------------------
-- problematic function (described later)
------------------------------
testx x = call (method1 x) $ OneTuple "test"

------------------------------
-- type classes
------------------------------
-- type class used to access "method1" associated function
class Method1 cls m func | cls -> m, cls -> func where 
    method1 :: cls -> m func

-- simplified version of type class used to "evaluate" functions based on 
-- their input. For example: passing empty tuple as first argument of `call` 
-- indicates evaluating function with default arguments (in this example 
-- the mechanism of getting default arguments is not available)
class Call a b where
    call :: a -> b

------------------------------
-- type classes instances
------------------------------
instance (out ~ (t1->t1)) => Method1 (Vector a) Vector_testid out where
  method1 = (Vector_testid . testid)

instance (base ~ (OneTuple t1 -> t2)) => Call (Vector_testid base) (OneTuple t1 -> t2) where
    call (Vector_testid val) = val

------------------------------
-- example usage
------------------------------
main = do
    let v = Vector (1::Int) (2::Int) (3::Int)
    -- following lines equals to a pseudocode of ` v.method1 "test" `
    -- OneTuple is used to indicate, that we are passing single element.
    -- In case of more or less elements, ordinary tuples would be used.
    print $ call (method1 v) $ OneTuple "test"
    print $ testx v

代码编译和GHC 7.6正常工作。 当我试图用GHC 7.7编译它时,出现以下错误:

debug.hs:61:10:
    Illegal instance declaration for
      ‛Method1 (Vector a) Vector_testid out’
      The liberal coverage condition fails in class ‛Method1’
        for functional dependency: ‛cls -> func’
      Reason: lhs type ‛Vector a’ does not determine rhs type ‛out’
    In the instance declaration for
      ‛Method1 (Vector a) Vector_testid out’

这个错误是由检查什么函数依赖可以做的新规则引起的,也就是liberal coverage condition (据我所知,这是通过使用-XUndecidableInstances放宽coverage condition

有些人试图解决这个问题

我试图通过改变Method1的定义来Method1这个问题:

class Method1 cls m func | cls -> m where 
    method1 :: cls -> m func

它解决了函数依赖关系的问题,但接下来是一行:

testx x = call (method1 x) $ OneTuple "test"

不再被允许,导致编译错误(7.6和7.7版本):

Could not deduce (Method1 cls m func0)
  arising from the ambiguity check for ‛testx’
from the context (Method1 cls m func,
                  Call (m func) (OneTuple [Char] -> s))
  bound by the inferred type for ‛testx’:
             (Method1 cls m func, Call (m func) (OneTuple [Char] -> s)) =>
             cls -> s
  at debug.hs:50:1-44
The type variable ‛func0’ is ambiguous
When checking that ‛testx’
  has the inferred type ‛forall cls (m :: * -> *) func s.
                         (Method1 cls m func, Call (m func) (OneTuple [Char] -> s)) =>
                         cls -> s’
Probable cause: the inferred type is ambiguous

编辑:

用类型族来解决这个问题也是不可能的(据我所知)。 如果我们用下面的代码(或类似的)替换Method1类型的类和实例:

class Method1 cls m | cls -> m where 
    type Func cls
    method1 :: cls -> m (Func cls)

instance Method1 (Vector a) Vector_testid where
    type Func (Vector a) = (t1->t1)
    method1 = (Vector_testid . testid)

我们会得到明显的错误Not in scope: type variable ‛t1' ,因为类型系列不允许使用类型,它不会出现在类型表达式的LHS上。

最后一个问题

我怎样才能使这个想法在GHC 7.7下工作? 我知道新liberal coverage condition允许GHC开发者在类型检查方面取得了一些进展,但它应该以某种方式在GHC 7.6中工作而不用编译器版本。

(没有强迫我的DSL用户引入任何更多的类型 - 到目前为止的一切,如类型类实例,我使用模板哈斯克尔)


这不是GHC 7.7中的错误。 当它允许违反函数依赖的实例时,它在GHC中是一个长期存在的错误。 幸运的是,这个问题终于得到解决。 GHC 7.7发出的错误信息非常详细,指出了实例Method1 (Vector a) Vector_testid out 。 回想一下函数依赖的含义。 特定

  class C a b | a -> b

因此,如果类型abb1使得C abC a b1都成立,那么bb1必定是相同的。 让我们看看你的实例:

  Method1 (Vector a) Vector_testid (t1->t1)

如果我们有满足Method1 (Vector Int) Vector_testid (b->b)Method1 (Vector a) Vector_testid (b1->b1)类型bb1 ,则完全不表示bb1必须相同。 因此你的实例是不合格的。 GHC 7.6和之前接受该计划的事实是GHC中一个众所周知的错误(每年讨论一次)。

你似乎在试图定义类似的东西

 Method1 (Vector a) Vector_testid (forall t. t -> t)

唉,这个语法是不允许的,虽然有许多工作循环存在。 例如,其中一个涉及Apply类(例如参见HList论文)。 更简单的方法如下

{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE FunctionalDependencies #-}

-- import Data.Tuple.OneTuple
newtype OneTuple x = OneTuple x deriving Show

------------------------------
-- data types
------------------------------
data Vector a = Vector {x :: a, y :: a, z :: a} deriving (Show)

-- testx x = call (method1 x) $ OneTuple "test"
testx x = call x Method1 $ OneTuple "test"

-- associate methods to classes
class Methods cls m x y | cls m x -> y where
  call :: cls -> m -> x -> y

instance Methods (Vector a) Method1 x x where
  call self _ x = x

data Method1 = Method1 -- method label
链接地址: http://www.djcxy.com/p/33343.html

上一篇: Liberal coverage condition introduced in GHC 7.7 breaks code valid in GHC 7.6

下一篇: Haskell could not unify type instance equations