本文介绍了GHC 7.7中引入的自由覆盖条件在GHC 7.6中有效的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这个想法



我正在写,编译为Haskell。

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

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

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

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

在这个例子中, method1 是一个与 Vector 数据类型,可以像 v.testid(5)(其中 v 是instance Vector 数据类型)。

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



问题



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



示例代码



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

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

导入Data.Tuple.OneTuple

------------------ ------------
- 数据类型
-------------------------- ----
data Vector a = Vector {x :: a,y :: a,z :: a}派生(显示)
- Vector_testid用作函数的包装器testid 。
newtype Vector_testid a = Vector_testid a

------------------------------
- 与数据类型Vector
关联的示例函数------------------------------
testid(v :: Vector a)x = x

----------------------------- -
- 有问题的函数(稍后介绍)
------------------------------
testx x = call(method1 x)$ OneTupletest

---------------------------- -
- 类型类
------------------------------
- 用于访问method1相关函数的类型类
class Method1 cls m func | cls - > m,cls - > func其中
method1 :: cls - > m func

- 用于根据
- 它们的输入评估函数的类型类的简化版本。例如:将空元组作为`call`的第一个参数传递
- 表示具有默认参数的评估函数(在本例中为
- 获取默认参数的机制不可用)
class呼叫ab其中
call :: a - > b

------------------------------
- 类类实例
------------------------------
instance(out〜(t1-> t1))=> ; Method1(Vector a)Vector_testid out其中
method1 =(Vector_testid。testid)

实例(base〜(OneTuple t1 - > t2))=> Call(Vector_testid base)(OneTuple t1 - > t2)其中
call(Vector_testid val)= val

----------------- -------------
- 示例用法
------------------------- -----
main = do
let v = Vector(1 :: Int)(2 :: Int)(3 :: Int)
- 后面的行等于伪代码`v.method1test`
- OneTuple用于表示我们正在传递单个元素。
- 在元素或多或少的情况下,将使用普通的元组。
print $ call(method1 v)$ OneTupletest
print $ testx v



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

  debug.hs:61:10 :
非法实例声明为
'Method1(Vector a)Vector_testid out'
自由报道条件在类'Method1'中失败
在函数依赖中:'cls - > func'
原因:lhs类型'Vector a'不确定rhs类型'out'

'实例声明中Method1(Vector a)Vector_testid out'

该错误是由检查函数依赖关系可以执行的新规则引起的,即自由覆盖条件(据我所知,这是通过使用 -XUndecidableInstances )放宽覆盖条件 p>

有些人试图解决这个问题



我试图通过更改 Method1 定义为:

  class Method1 cls m func | cls  - > m其中
method1 :: cls - > m func

解决了函数依赖性的问题,但是接下来是行:

  testx x = call(method1 x)$ OneTupletest

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

 无法推论(Method1 cls m func0)
从上下文中的'testx'
的歧义检查中产生(Method1 cls m func,
Call(m func)(OneTuple [Char ( - )] - > s))
由'testx'的推断类型约束:
(Method1 cls m func,Call(m func)(OneTuple [Char] - > s))=>
cls - > s
在debug.hs:50:1-44
类型变量'func0'不明确
当检查'testx'
具有推断类型'forall cls(m :: * - > *)func s。
(Method1 cls m func,Call(m func)(OneTuple [Char] - > s))=>
cls - > s'
可能的原因:推断的类型不明确

编辑: strong>



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

  class Method1 cls m | cls  - > m其中
类型Func cls
method1 :: cls - > m(Func cls)

实例Method1(Vector a)Vector_testid其中
类型Func(Vector a)=(t1-> t1)
method1 =(Vector_testid。testid)

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



最后一个问题

strong>



我怎样才能让这个想法在GHC 7.7下工作?我知道新的自由报道条件允许GHC开发者在类型检查方面取得一些进展,但它应该以某种方式在GHC 7.6中工作而不用编译器版本。 (不强制我的DSL的用户引入任何更多的类型 - 到目前为止的一切,如类型类实例,我使用模板哈斯克尔)$ b $
$ b

b

解决方案

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

  class C a b | a  - > b 

如果类型 a , b 和 b1 是这样的: C ab 和 C a b1
都成立,它必须是真的 b 和 b1 是一样的。让我们来看看你的实例:
$ b $ pre $ Method1(Vector a)Vector_testid(t1-> t1)

如果我们有类型 b 和 b1 方法1(Vector Int)Vector_testid(b-> b)
和方法1(向量a)Vector_testid (b1-> b1),完全没有意味着必须 b 和 b1 必须是相同的。因此你的实例是不合格的。 GHC 7.6和之前接受该计划的事实是GHC中一个众所周知的错误(每年讨论一次)。


你似乎在试图定义一些东西例如

 方法1(向量a)Vector_testid(全部t  - > t)

唉,这个语法是不允许的,尽管很多workrounds存在。例如,其中一个涉及Apply类(例如参见HList论文)。更简单的方法如下:

  { - #LANGUAGE FlexibleInstances# - } 
{ - #LANGUAGE ScopedTypeVariables# - }
{ - #LANGUAGE TypeFamilies# - }
{ - #LANGUAGE UndecidableInstances# - }
{ - #LANGUAGE FunctionalDependencies# - }
$ b $ - import Data.Tuple .OneTuple
newtype OneTuple x = OneTuple x派生显示

---------------------------- -
- 数据类型
------------------------------
data Vector a = Vector {x :: a,y :: a,z :: a}派生(显示)

- testx x =调用(method1 x)$ OneTupletest
testx x =调用x Method1 $ OneTupletest

- 将方法关联到类
class方法cls mxy | cls m x - > y其中
call :: cls - > m - > x - > y

实例方法(向量a)方法1 xx其中
自我调用_ x = x

data方法1 =方法1 - 方法标签


The idea

I'm writing a DSL, which compiles to Haskell.

Users of this language can define own immutable data structures and associated functions. By associated function I mean a function, which belongs to a data structure.For example, user can write (in "pythonic" pseudocode):

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

(which is equivalent to the following code, but shows also, that associated functions beheva like type classes with open world assumption):

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

In this example, method1 is a function associated with Vector data type, and can be used like v.testid(5) (where v is instance of Vector data type).

I'm translating such code to Haskell code, but I'm facing a problem, which I'm trying to solve for a long time.

The problem

I'm trying to move the code from GHC 7.6 over GHC 7.7 (which is pre-release of 7.8) (Newer versions can be compiled from sources). The code works perfectly under GHC 7.6, but does not under GHC 7.7.I want to ask you how can I fix it to make it working in the new version of the compiler?

Example code

Lets see a simplified version of generated (by my compiler) Haskell code:

{-# 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

The code compiles and works fine with GHC 7.6. When I'm trying to compile it with GHC 7.7, I'm getting following error:

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’

The error is caused by new rules of checking what functional dependencies can do, namely liberal coverage condition (as far as I know, this is coverage condition relaxed by using -XUndecidableInstances)

Some attemps to fix the problem

I was trying to overcome this problem by changing the definition of Method1 to:

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

Which resolves the problem with functional dependencies, but then the line:

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

is not allowed anymore, causing a compile error (in both 7.6 and 7.7 versions):

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

EDIT:

It is also impossible to solve this issue using type families (as far as I know). If we replace Method1 type class and instances with following code (or simmilar):

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)

We would get obvious error Not in scope: type variable ‛t1’, because type families does not allow to use types, which does not appear on LHS of type expression.

The final question

How can I make this idea work under GHC 7.7? I know the new liberal coverage condition allows GHC devs make some progress with type checking, but it should somehow be doable to port idea working in GHC 7.6 over never compiler version.

(without forcing user of my DSL to introduce any further types - everything so far, like type class instances, I'm genarating using Template Haskell)

解决方案

This is not a bug in GHC 7.7. It was a long-standing bug in GHC when it allowed instancesthat violate functional dependencies. It seems, fortunately, that this problem is finally fixed. The error message emitted by GHC 7.7 is quite detailed, pointing out the problem with your instance Method1 (Vector a) Vector_testid out. Recall the meaning of functionaldependencies. Given

  class C a b | a -> b

it follows that if the types a, b and b1 are such that C a b and C a b1 both hold, it must be true that b and b1 are the same. Let's look at your instance:

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

If we have types b and b1 that satisfy Method1 (Vector Int) Vector_testid (b->b)and Method1 (Vector a) Vector_testid (b1->b1), nothing at all implies that b and b1 must be the same. Hence your instance is ill-formed. The fact that GHC 7.6 and before accepted the program was a well-known bug in GHC (discussed about every year).

What you seem to be trying is to define something like

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

Alas, this syntax is not allowed, although many worksrounds exist. One, for example, involves the Apply class (see the HList paper, for example). A simpler way is as follows

{-# 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

这篇关于GHC 7.7中引入的自由覆盖条件在GHC 7.6中有效的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-23 14:46