由于Paterson的条件,无法解析Haskell类型类约束

时间:2018-08-17 14:32:35

标签: haskell typeclass undecidable-instances

我正在尝试使用索引嵌套注释构建AST。我在顶级添加了一个用于剥离注释的类型类,并尝试提供默认实例,该实例有效地表示:“如果您知道如何剥离注释,那么您将知道如何在特定的AST节点处剥离注释。”

由于我的树节点之一是一个Nat索引的谓词,并且其父节点存在地对该变量进行量化,因此当我尝试为父节点编写实例时,我陷入了Paterson的状况。也就是说,我的断言中的类型变量比头部中的要多。

如果我打开UndecidableInstances,则GHC无法统一类型​​为Nat的变量。

如果我进一步打开AllowAmbiguousTypes,则会收到一个更荒谬的错误,该错误表明尽管找到的实例位于类型实例的断言中,但它找不到实例。

我的问题是:

  1. 这实际上是一个不好写的实例,还是类型检查器过于保守?
  2. 如果情况不好或无法解决问题,我还能如何提供这种默认的剥皮行为?

这是最少的代码(我剥离了对于类型错误而言并非必不可少的位,因此某些位可能看起来是多余的):

{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE AllowAmbiguousTypes #-}

module Constraint where

data AnnType = ABase

data family PredicateAnn (a :: AnnType)
data instance PredicateAnn (a :: AnnType) = PABase

data Nat = Zero | Succ Nat

data Predicate (n :: Nat) (a :: AnnType) = Predicate
data Literal   (a :: AnnType) = forall n. Literal (Predicate n a)

class PeelableAST (ast :: AnnType -> *) (ann :: AnnType -> AnnType) where
  peel :: ast (ann a) -> ast a

class PeelableAnn (f :: AnnType -> *) (ann :: AnnType -> AnnType) where
  peelA :: f (ann a) -> f a

instance PeelableAST (Predicate n) ann
         => PeelableAST Literal ann where
  peel (Literal predicate) = Literal (peel predicate)

instance PeelableAnn PredicateAnn ann => PeelableAST (Predicate n) ann where
  peel Predicate = Predicate

这是没有UndecidableInstances的确切错误:

src/Constraint.hs:27:10: error:
• Variable ‘n’ occurs more often
    in the constraint ‘PeelableAST (Predicate n) ann’
    than in the instance head
  (Use UndecidableInstances to permit this)
• In the instance declaration for ‘PeelableAST Literal ann’
   |
27 | instance PeelableAST (Predicate n) ann
   |          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^...

这里是一个:

src/Constraint.hs:28:10: error:
• Could not deduce (PeelableAST (Predicate n0) ann)
  from the context: PeelableAST (Predicate n) ann
    bound by an instance declaration:
               forall (n :: Nat) (ann :: AnnType -> AnnType).
               PeelableAST (Predicate n) ann =>
               PeelableAST Literal ann
    at src/Constraint.hs:(28,10)-(29,35)
  The type variable ‘n0’ is ambiguous
• In the ambiguity check for an instance declaration
  To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
  In the instance declaration for ‘PeelableAST Literal ann’
   |
28 | instance PeelableAST (Predicate n) ann
   |          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^...

这里是AllowAmbiguousTypes的人:

 src/Constraint.hs:31:39: error:
• Could not deduce (PeelableAnn PredicateAnn ann)
    arising from a use of ‘peel’
  from the context: PeelableAST (Predicate n) ann
    bound by the instance declaration
    at src/Constraint.hs:(29,10)-(30,35)
• In the first argument of ‘Literal’, namely ‘(peel predicate)’
  In the expression: Literal (peel predicate)
  In an equation for ‘peel’:
      peel (Literal predicate) = Literal (peel predicate)
   |
31 |   peel (Literal predicate) = Literal (peel predicate)
   |                                       ^^^^^^^^^^^^^^

编辑:

正如丹尼尔·瓦格纳(Daniel Wagner)所提出的,一种解决方案是在PeelableAnn PredicateAnn ann实例中将PeelableAST Literal ann声明为断言。但是,我从不使用peelA定义中PeelableAnn定义的PeelableAST Literal ann,并且我希望此实例具有默认行为,并可以通过直接提供一个PeelableAST (Predicate n) ann实例来覆盖它。换句话说,剥离可能是与生俱来的。

由于PeelableAnn PredicateAnn ann要求PeelableAST (Predicate n) ann,所以我认为GHC应该能够找到并满足此条件。

我可以简单地拥有一个伪造的PeelableAnn PredicateAnn ann实例,而只是被更具体的实例所忽略,但这确实很hacky

1 个答案:

答案 0 :(得分:3)

在您的PeelableAST Literal ann实例中,您使用PeelableAST (Predicate n) ann实例。如果类型检查器要使用该实例,则它必须验证其前提,即PeelableAnn PredicateAnn ann成立。但这并不知道,因为您尚未将其设为PeelableAST Literal ann实例的先决条件。

没关系;它很容易修复,可让您完全避免使用歧义类型。只需添加它担心的前提作为您PeelableAST Literal ann实例的前提即可。确实,由于现在这是两个实例的前提条件,因此您也可以删除PeelableAnn PredicateAnn ann前提条件,因为这种新的更强大的条件暗示了该前提条件。所以:

instance PeelableAnn PredicateAnn ann => PeelableAST Literal ann where
    peel (Literal predicate) = Literal (peel predicate)

您可以删除AllowAmbiguousTypes,尽管仍然需要UndecidableInstances,因为PeelableAnn PredicateAnn ann在结构上显然不小于PeelableAST Literal ann