首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >虽然我使用了相关的类型同义词,但ghc响应无法从上下文xx中推断出oo

虽然我使用了相关的类型同义词,但ghc响应无法从上下文xx中推断出oo
EN

Stack Overflow用户
提问于 2013-07-21 00:18:54
回答 3查看 305关注 0票数 2

我使用类型族重构了我的代码,下面是项目中使用的类型类。

代码语言:javascript
复制
class HeukaryaGene (d :: *) where
  type TypeGeneStr d :: *
  type TypeGeneRep d :: *
  lexByArrow   :: TypeGeneStr d -> [TypeGeneStr d]
  geneTypeRep  :: d -> TypeGeneRep d
  geneTypeRepArgs :: TypeGeneRep d -> [TypeGeneRep d]
  showGeneTypeRep :: TypeGeneRep d -> TypeGeneStr d
  showExpandTypeArgs :: d -> [TypeGeneStr d]
  showExpandTypeArgs dynam = lexByArrow typo
    where
    typo = showGeneTypeRep $ geneTypeRep dynam :: TypeGeneStr d

但是ghc总是抱怨很多相同的事情:

代码语言:javascript
复制
Could not deduce (TypeGeneStr d ~ TypeGeneStr d2)
from the context (HeukaryaGene d)
  bound by the class declaration for `HeukaryaGene'
  at AI/Heukarya/Gene.hs:(22,1)-(42,63)
NB: `TypeGeneStr' is a type function, and may not be injective
The type variable `d2' is ambiguous
Possible fix: add a type signature that fixes these type variable(s)
Expected type: [TypeGeneStr d]
  Actual type: [TypeGeneStr d2]
In the return type of a call of `lexByArrow'
In the expression: lexByArrow typo
In an equation for `showExpandTypeArgs':
    showExpandTypeArgs dynam
      = lexByArrow typo
      where
          typo = showGeneTypeRep $ geneTypeRep dynam :: TypeGeneStr d

我想知道我误解了什么

EN

回答 3

Stack Overflow用户

发布于 2013-07-21 01:31:22

您不能从TypeGeneStr d这样的类型族返回到原始类型d。当GHC说"NB:`TypeGeneStr'是一个类型函数,可能不是内射的“时,这就是它告诉你的:只要知道TypeGeneStr d ~ TypeGeneStr d'并不意味着d ~ d' (其中~是类型相等)。

因此,如果您的类型类函数的名称中只有类型同义词,并且从不引用原始参数,那么您将永远无法调用它-这将描述您的类型类中除geneTypeRep和无法实现的showExpandTypeArgs之外的所有函数。失败的原因是GHC不能确定使用类型类的哪个实例。考虑一下如果我允许TypeGeneStr和/或TypeGeneRep发生冲突会发生什么,如下例所示:

代码语言:javascript
复制
class HeukaryaGene Int where
  type TypeGeneStr Int = String
  type TypeGeneRep Int = ()
  showGeneTypeRep () = "Int"

class HeukaryaGene Bool where
  type TypeGeneStr Bool = String
  type TypeGeneRep Bool = ()
  showGeneTypeRep () = "Bool"

那么showGeneTypeRep ()应该是"Int"还是"Bool"?或者它甚至应该是一个String?如果我有

代码语言:javascript
复制
class HeukaryaGene () where
  type TypeGeneStr () = ()
  type TypeGeneRep () = ()
  showGeneTypeRep () = ()

那么showGeneTypeRep ()也可以是()。这就是你对showExpandTypeArgs的定义:在typo内部,虽然GHC可以正确地确定dynam :: d,从而确定geneTypeRep dynam :: TypeGeneRep d,但它不知道应该选择哪个showGeneTypeRep :: TypeGeneRep d -> TypeGeneStr d。您对lexByArrow的调用遇到了类似的问题: GHC只知道某个d'typo :: TypeGeneStr d',而它不知道选择哪个版本的lexByArrow :: TypeGeneStr d' -> [TypeGeneStr d'] (它确实知道TypeGeneRep d ~ TypeGeneRep d',但这不足以决定)。

在我的GHC (7.4.2)上,我也得到了一个错误,即Typo的类型签名中的d与类型类头中的d不同,但是删除该类型签名(显然)并不能得到类型检查的结果。

(我应该说:您在这里得到的错误消息肯定是令人困惑的,因为它们不清楚发生了什么以及为什么会出现错误。)

我能想到的最简单的解决方法是为所有不接受d参数的函数包含一个虚拟参数。您永远不会分析这个参数;它只是用来引导实例选择。在调用点,您可以对某些具体类型d使用undefined :: d。它看起来像这样:

代码语言:javascript
复制
class HeukaryaGene (d :: *) where
  type TypeGeneStr d :: *
  type TypeGeneRep d :: *
  lexByArrow   :: d -> TypeGeneStr d -> [TypeGeneStr d]
  geneTypeRep  :: d -> TypeGeneRep d
  geneTypeRepArgs :: d -> TypeGeneRep d -> [TypeGeneRep d]
  showGeneTypeRep :: d -> TypeGeneRep d -> TypeGeneStr d
  showExpandTypeArgs :: d -> [TypeGeneStr d]
  showExpandTypeArgs dynam = lexByArrow dynam typo
    where
    typo = showGeneTypeRep dynam $ geneTypeRep dynam

如果你不喜欢这一点,我能想到的最小侵入性的修复方法是从类型族到数据族。数据族类似于类型族,只是您使用了关键字data,并且它们定义了全新的数据类型。这一点很重要:就像普通的数据结构一样,数据族是生成性的,因此是内射性的。(数据族的每个实例化都会生成一个全新的类型。)它看起来像这样:

代码语言:javascript
复制
class HeukaryaGene (d :: *) where
  data TypeGeneStr d :: *
  data TypeGeneRep d :: *
  lexByArrow   :: TypeGeneStr d -> [TypeGeneStr d]
  geneTypeRep  :: d -> TypeGeneRep d
  geneTypeRepArgs :: TypeGeneRep d -> [TypeGeneRep d]
  showGeneTypeRep :: TypeGeneRep d -> TypeGeneStr d
  showExpandTypeArgs :: d -> [TypeGeneStr d]
  showExpandTypeArgs dynam = lexByArrow typo
    where
    typo = showGeneTypeRep $ geneTypeRep dynam

我所做的唯一更改是将type更改为data,并删除了typo的类型签名。这个版本的关键之处在于,要现在实例化HeukaryaGene,您必须编写如下内容

代码语言:javascript
复制
instance HeukaryaGene Int where
  -- You can instantiate data families with newtypes, too.
  newtype TypeGeneStr Int = TGSInt String
  data    TypeGeneRep Int = TGRInt
  showGeneTypeRep TGRInt = TGSInt "Int"

也就是说,潜在地有很多(取消)包装要做。但这将会起作用。

您也可以在这里使用函数依赖,但您实际上是在复制此解决方案及其缺点;其思想是您将拥有class HeukaryaGene d tgs tgr | d -> tgs tgr, tgs -> d, tgr -> d,因此知道dtgstgr中的任何一个就足以推断出其他两个。这看起来不错,但这意味着任何给定的类型只能作为基因字符串或基因代表类型使用一次,因此本质上具有与上面的数据系列版本相同的缺点。

另一种解决方案是,如果您从不在showExpandTypeArgs外部调用lexByArrowshowGeneTypeRep,则将它们从类型类中移除,并让用户以其方式实现showExpandTypeArgs。然而,这对geneTypeRepArgs没有帮助,它将不得不删除

最后一个解决方案是不使用类型类,而是自己处理字典。这将是一个非常彻底的重新设计(尽管不一定是一个糟糕的设计),但是如果您真的想要TypeGeneStrTypeGeneRep的类型同义词,这是我能想到的唯一方法。它看起来像这样:

代码语言:javascript
复制
data HeukaryaGene d tgs tgr =
  HeukaryaGene { lexByArrow      :: tgs -> [tgs]
               , geneTypeRep     :: d -> tgr
               , geneTypeRepArgs :: tgr -> [tgr]
               , showGeneTypeRep :: tgr -> tgs }

showExpandTypeArgs :: d -> [tgs]
showExpandTypeArgs dynam = lexByArrow typo
  where typo = showGeneTypeRep $ geneTypeRep dynam

然后,以前类型为HeukaryaGene d => t的函数将具有类型HeukaryaGene d tgs tr -> t。在这里,使用-XRecordWildCards (和as-patterns)会有所帮助,允许您编写

代码语言:javascript
复制
getTypeRepArgs HeukaryaGene{..} = geneTypeRepArgs . geneTypeRep

如果你想让一些字典有一个不同的showExpandTypeArgs实现,你必须稍微改变一下结构:

代码语言:javascript
复制
data HeukaryaGene d tgs tgr =
  HeukaryaGene { lexByArrow         :: tgs -> [tgs]
               , geneTypeRep        :: d -> tgr
               , geneTypeRepArgs    :: tgr -> [tgr]
               , showGeneTypeRep    :: tgr -> tgs
               , showExpandTypeArgs :: d -> [tgs] }

showExpandTypeArgsDefault :: HeukaryaGene d tgs tgr -> d -> [tgs]
showExpandTypeArgsDefault HeukaryaGene{..} dynam = lexByArrow typo
  where typo = showGeneTypeRep $ geneTypeRep dynam

然后,您将在初始化期间打结:

代码语言:javascript
复制
hgInt :: HeukaryaGene Int String ()
hgInt = HeukaryaGene { lexByArrow         = words
                     , geneTypeRep        = const ()
                     , geneTypeRepArgs    = const []
                     , showGeneTypeRep    = const "Int"
                     , showExpandTypeArgs = showExpandTypeArgsDefault hgInt }

实际上,在这个解决方案中,dgeneTypeRep中可能是多余的,因此在showExpandTypeArgs中是多余的,在HeukaryaGene本身中也是多余的:如果像在Typeable中一样,您只是使用d来挑选类型类,那么在这里就不需要它了。但这将取决于您的具体情况。

票数 4
EN

Stack Overflow用户

发布于 2013-07-21 02:59:21

我发现这个优雅的解决方案是这样的:Incomprehensible error message with type families

类型类定义:

代码语言:javascript
复制
class (
  UnTypeGeneStr (TypeGeneStr d) ~ d, UnTypeGeneRep (TypeGeneRep d) ~ d
  ) => HeukaryaGene d where
  type TypeGeneStr d :: *
  type TypeGeneRep d :: *
  type UnTypeGeneStr o :: *
  type UnTypeGeneRep o :: *

实例定义:

代码语言:javascript
复制
instance HeukaryaGene Dynamic where
  type TypeGeneStr Dynamic = String
  type TypeGeneRep Dynamic = TypeRep
  type UnTypeGeneStr String  = Dynamic
  type UnTypeGeneRep TypeRep = Dynamic

感谢大家的帮助!!

票数 1
EN

Stack Overflow用户

发布于 2017-03-24 02:55:43

我知道这是一个老问题,但是GHC8.0增加了对injective type synonyms的支持。如果您启用了TypeFamilyDependencies,您的示例应该可以使用以下内容:

代码语言:javascript
复制
class HeukaryaGene d where
  type TypeGeneStr d = s | s -> d
  type TypeGeneRep d = r | r -> d
  ...
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/17764157

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档