GHC 7.7 中引入的自由覆盖条件破坏了 GHC 7.6 中有效的代码

2023-11-25

The idea

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

该语言的用户可以定义自己的不可变数据结构和关联函数。我所说的关联函数是指属于数据结构的函数。 例如,用户可以编写(用“pythonic”伪代码):

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

(这相当于下面的代码,但也显示了关联函数的行为类似于具有开放世界假设的类型类):

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

在这个例子中,method1是一个与关联的函数Vector数据类型,并且可以像这样使用v.testid(5) (where v是的实例Vector数据类型)。

我正在将此类代码翻译为 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(据我所知,这是coverage condition通过使用放松-XUndecidableInstances)

解决问题的一些尝试

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

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

EDIT:

使用类型族来解决这个问题也是不可能的(据我所知)。如果我们更换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 用户引入任何其他类型 - 到目前为止的所有内容,例如类型类实例,我正在使用 Template Haskell 生成)


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

  class C a b | a -> b

由此可见,如果类型a, b and b1是这样的C a b and C a b1两者都成立,那么一定是真的b and b1是相同的。让我们看看您的实例:

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

如果我们有类型b and b1满足Method1 (Vector Int) Vector_testid (b->b) and Method1 (Vector a) Vector_testid (b1->b1),没有任何暗示b and b1必须是相同的。因此您的实例格式不正确。 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
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

GHC 7.7 中引入的自由覆盖条件破坏了 GHC 7.6 中有效的代码 的相关文章

  • 哈斯克尔状态单子

    是否putState Monad 的函数会更新实际状态还是仅返回具有新值的新状态 我的问题是 State Monad 可以在命令式设置中像 全局变量 一样使用吗 并且确实put修改 全局变量 我的理解是 不 它不会修改初始状态 但是使用单子
  • 当单态限制打开*时,如何解决歧义问题?

    因此 在学习 Haskell 时 我很快就遇到了可怕的单态限制 在 ghci 中 Prelude gt let f print show Prelude gt f 5
  • 副作用是纯函数中找不到的一切吗?

    可以肯定地说 以下二分法成立 每个给定的函数是 要么纯粹 或有副作用 如果是这样 函数的 副作用就是纯函数中找不到的任何东西 这很大程度上取决于您选择的定义 可以公平地说 函数是pure or impure 纯函数始终返回相同的结果并且不会
  • 表达式“ap zip tail”如何工作

    我想知道怎么写f x zip x tail x 点免费 所以我使用了pointfree程序 结果是f ap zip tail ap作为 Control Monad 的函数 我不明白点自由定义是如何工作的 如果我能从类型的角度去理解的话 希望
  • 可以通过Data.Function.fix来表达变形吗?

    我有这个可爱的fixana这里的函数执行速度比她的姐妹快 5 倍左右ana 我有一个criterion报告支持我这一点 ana alg Fix fmap ana alg alg fixana alg fix f gt Fix fmap f
  • Haskell printf 转字符串

    Haskell 中有等效的 sprintf 吗 我需要将双精度值转换并格式化为字符串 有没有其他方法而不使用printf什么样的功能 主要问题是要避免 Prelude gt putStrLn myDoubleVal 1 7944444444
  • 'lens' 的阴谋集团依赖性解析失败

    我刚刚做了一个阴谋更新并尝试从 hackage 安装 lens 这给了我以下错误 cabal install j lens Resolving dependencies Configuring dlist 0 7 0 1
  • “反向”使用 Maybe Monad

    假设我有很多功能 f a gt Maybe a g a gt Maybe a h a gt Maybe a 我想按以下方式组合它们 如果 f 返回 Nothing 则计算 g 如果 g 返回 Nothing 则计算 h 如果其中任何一个计算
  • 将 Either 列表转换为其中包含列表的 Either 列表

    我是 Haskell 的初学者 我正在编写一些使用 Haskell 的代码Either https hackage haskell org package base 4 9 0 0 docs Data Either html用于错误处理 E
  • GHC 是否使用存在类型的动态调度?

    下面的代码是否使用了 C 或 Java 中所理解的动态调度 据我了解 在最后一行 编译器不可能在编译时知道要调用哪个 实现 但代码会编译并产生正确的结果 有人可以解释一下 这背后有什么样的实现 例如 vptr 吗 LANGUAGE Exis
  • 在 haskell 中处理 IO 与纯代码

    我正在编写一个shell脚本 我在haskell中的第一个非示例 它应该列出一个目录 获取每个文件大小 进行一些字符串操作 纯代码 然后重命名一些文件 我不确定我做错了什么 所以有两个问题 我应该如何安排这样的程序中的代码 我有一个具体问题
  • 存在函数依赖关系时类型推断如何工作

    考虑下面的代码 LANGUAGE MultiParamTypeClasses FlexibleInstances FunctionalDependencies UndecidableInstances FlexibleContexts cl
  • Haskell 错误处理方法

    毫无疑问 Haskell 中有多种机制来处理错误并正确处理它们 错误单子 要么 也许 异常等 那么为什么用其他语言编写容易出现异常的代码比用 Haskell 感觉更简单呢 假设我想编写一个命令行工具来处理命令行上传递的文件 我想 验证提供的
  • 带有查询参数的渲染 url

    无法找到简单问题的解决方案 答案应该是显而易见的 如何在 hamlet 模板中使用查询参数渲染 url I e ItemsR 将生成http localhost 3000 items我如何生成类似的东西http localhost 3000
  • 如何处理“恐慌:不可能的事情发生了”并在 Haskell 中继续

    我有以下代码 它使用 GHC API 加载模块并获取表达式的类型 typeObjects String gt String gt IO Type typeObjects modules objects do defaultErrorHand
  • 使用 Haskell 将函数注入到 Java .class 文件中

    我使用 Haskell 编写了一个 Java 字节码解析器 它工作得很好 然而下一步让我完全难住了 我的 Haskell 程序需要修改 class 文件 以便在执行时 Java 程序打印 输入 此处的方法名称 在执行方法之前 并且 退出 此
  • 简单的秒差距示例会产生类型错误

    我正在尝试编译这个简单的秒差距代码 import Text Parsec simple letter 但我不断收到此错误 No instance for Stream s0 m0 Char arising from a use of let
  • 为什么解析器组合器“seq”用“bind”和“return”定义?

    我正在读这个article http eprints nottingham ac uk 237 1 monparsing pdf关于解析器组合器并且不理解以下内容 他们说使用seq 见下文 导致解析器将嵌套元组作为结果 操作起来很混乱 se
  • 将名称绑定到值与将值分配给变量

    阅读 Bartosz Milewski 的文章完整的 https www fpcomplete com school starting with haskell basics of haskell 3 pure functions lazi
  • 为什么以下内容会并行运行而不是顺序运行?

    给定以下函数evalPair parPair and deepSeq分别 evalPair Strategy a gt Strategy b gt Strategy a b evalPair sa sb a b do a lt sa a b

随机推荐

  • 制作一个android地图菜单来改变地图类型

    我的 Android 应用程序中有一张地图 默认情况下 它显示卫星视图 但我已将其关闭以仅显示路线图视图 但是 我想知道如何构建一个菜单 以便当用户按下菜单按钮时 它会在底部显示一个带有 切换卫星地图 的部分 我将来会在菜单中添加其他项目
  • Linq-to-sql 不包含或不在?

    我正在构建一个民意调查小部件 我有 2 个表 分别称为 Polls 和 PollsCompleted 我需要执行 linq 查询来获取 PollsCompleted 中给定用户不存在的所有民意调查 我有以下几套 民意调查 其中 Active
  • 在编译时重命名符号,无需以跨平台方式更改代码

    在创建静态对象时 是否可以在编译时以跨平台方式重命名符号 无需更改代码 我最近推荐了 objcopy 但 linux 不是唯一的目标平台 它也必须在 mac 上运行 我正在使用 gcc 进行编译 所以我希望有某种 gcc 选项 我听说过 d
  • Proguard 回溯丢失的行号

    我正在尝试从我的 Android 应用程序中反混淆堆栈跟踪 我在构建应用程序时使用了 proguard 并且运行 retrace 似乎或多或少有效 不起作用的是解码行号 输出上不显示行号 它列出了每个 at 的多个选择 这是我的 progu
  • Ember Data 嵌套资源 URL

    假设我有一个具有以下布局的 Rails 应用程序 从我的实际项目中对此进行了一些简化 User has many Notes Category has many Notes Note belongs to User belongs to C
  • 在 PHP 中为逗号分隔的字符串添加引号

    我有一个表单 它是一个选择多个输入的表单 它发布如下值 option1 option2 option3 etc 将其转换为的最佳方法是什么 option1 option2 option3 etc 目前我正在这样做 但感觉不对 variabl
  • Spring Boot 升级错误 - 名称为 org.springframework.transaction.config.internalTransactionalEventListenerFactory 的 bean 定义无效

    在尝试在基于 java 的 Web 应用程序中将 spring boot 升级到最新版本 2 1 1 并将 spring 框架升级到 5 1 3 时 出现以下错误 Invalid bean definition with name org
  • Sqlalchemy:使用多个 filter() 调用生成 OR 子句

    我是 sqlalchemy 的新手 需要一些帮助 我正在尝试编写一个小型应用程序 我必须为其动态更改选择语句 所以我这样做s select files 然后我添加过滤器s s where files c createtime between
  • 终端启动时出现错误消息[重复]

    这个问题在这里已经有答案了 每次启动终端时我都会收到此错误消息 bash usr local bin usr local bin usr bin bin usr sbin sbin opt X11 bin No such file or d
  • Java:IndentingXMLStreamWriter 替代方案?

    我正在使用 StAX 创建一个相当大的 xml 文档 到目前为止 我一直在使用 IndentingXMLStreamwriter 类来获取格式良好的文档 另请参阅这个答案 几天前 我们使用较旧的 jdk 版本 6 26 设置了一个 jenk
  • JSF 更新复合组件

    是否可以仅通过指定父复合 ID 来更新复合组件的子组件 例如 如果我有
  • appcfg.py 显示您必须以管理员身份登录

    当我尝试通过以下方式将示例 csv 数据上传到我的 GAE 应用程序时appcfg py 它显示以下 401 错误 2015 11 04 10 44 41 820 INFO client py 571 Refreshing due to a
  • 如何在 Selenium WebDriver 中自动清除浏览器缓存?

    如何在每次测试运行之前清除浏览器缓存 我尝试过driver manage deleteAllCookies in setUp创建驱动程序实例后的方法 它适用于 Firefox 但对于 IE 不起作用 有没有IE的解决办法 请提供给我 您可以
  • .NET 中的跨进程读写同步原语?

    是否有跨进程工作的读 写锁定机制 类似于互斥锁 但读 写而不是独占锁定 我想允许并发读取访问 但允许独占写入访问 Windows 不包含跨进程读写锁 可以使用信号量和互斥量的组合来构造互斥量 互斥量由写入者持有以进行独占访问 或者由读取者持
  • 函数指针如何工作?

    我正在问一些具体问题 如何在类中初始化它们 如何将函数作为参数传递 类中是否需要声明和定义函数指针 对于问题 2 我的意思是 void s void void f function what should I put as type to
  • 通过 jquery 上下移动选择选项

    所以我让这段代码适用于 Firefox 和 Chrome 它的作用是允许您在 HTML 选择表单中重新排序选项 但是当我通过 IE8 测试代码时 它有点不完整 它仅适用于前几次单击 之后您必须多次单击该按钮才能使其工作 有谁知道任何其他代码
  • 解析 HTML 表格最快、最简单、最好的方法是什么?

    我正在尝试获取这张桌子http www datamystic com timezone time zones html转换为数组格式 这样我就可以用它做任何我想做的事情 最好是 PHP Python 或 JavaScript 这种问题经常出
  • RxJs 可观察分页

    第一 这是我使用 RxJs 的第一个项目 我想通过使用它我会学到最好的东西 我找到了这个答案 使用 RxJs 将分页请求转换为 Observable 流但评论里却说 您仍然超出了最大调用堆栈 返回大约 430 页 我认为递归可能不是最好的解
  • Shift + 鼠标滚轮水平滚动

    对于水平滚动 使用 Shift 滚轮相当常见 这两者都相当容易捕获 我可以使用 MouseWheel 事件以及由 KeyDown KeyUp 事件设置的标志来跟踪何时按下 Shift 键 但是 如何真正触发水平滚动呢 我知道 WM MOUS
  • GHC 7.7 中引入的自由覆盖条件破坏了 GHC 7.6 中有效的代码

    The idea 我正在写一个DSL 编译为 Haskell 该语言的用户可以定义自己的不可变数据结构和关联函数 我所说的关联函数是指属于数据结构的函数 例如 用户可以编写 用 pythonic 伪代码 data Vector a x y