存在类型歧义



为这个有点做作的例子道歉。我试图在不失去理由的情况下尽可能简化这一点:

假设我有一个多参数类型类Relation:

class Relation r a b where ....

以及一个在这种类型上存在量化的函数:

neighbours :: forall r a b. Relation r a b => a -> r -> Graph -> [b]

现在我介绍几个Relation:的实例

data Person = Person String
data Pet = Pet String
data Owns = Owns
data Desires = Desires
instance Relation Owns Person Pet
instance Relation Desires Person Pet

现在,我想写一个方法,通过某种方式获取与给定人相关的所有宠物,而不暴露实现的复杂性:

data PetAttachment = AttachOwns | AttachDesires
personPets :: Person -> PetAttachment -> Graph -> [Pet]
personPets person att g = 
    neighbours person r g
  where
    r = case PetAttachment of
      AttachOwns -> Owns
      AttachDesires -> Desires

我的问题是:如何正确地键入r。在没有提示的情况下,它将尝试键入Owns,因此失败。我也可以提示它应该是存在类型的:

r :: forall r. Relation r Person Pet => r

但编译器似乎无法推断出Relation r0 Person Pet,称r0是模糊的:

关系。hs:26:5:没有(关系r0个人宠物)的实例因使用"邻居"而产生

类型变量"r0"的不明确

我能说服r以这样一种方式键入,让它编译吗?

完整的可编译示例:

{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleContexts #-}
data Graph = Graph
class Relation r a b where
  relation :: (r,a,b) -> Int
neighbours :: forall r a b. Relation r a b => a -> r -> Graph -> [b]
neighbours = undefined
data Person = Person String
data Pet = Pet String
data Owns = Owns
data Desires = Desires
instance Relation Owns Person Pet where
  relation _ = 1
instance Relation Desires Person Pet where
  relation _ = 2
data PetAttachment = AttachOwns | AttachDesires
personPets :: Person -> PetAttachment -> Graph -> [Pet]
personPets person att =
    neighbours person r
  where
    r :: forall r. Relation r Person Pet => r
    r = case att of
      AttachOwns -> Owns
      AttachDesires -> Desires
main :: IO ()
main = undefined

这里有两种编写方法,一种使用GADT,另一种使用Rank 2 Type。

GADT

GADT可以捕获对r的约束,即必须存在Relation r Person Pet的实例。要使用GADT,您需要添加

{-# LANGUAGE GADTs #-}

由于Relation r a b =>对构造函数的约束,RelationOf a b将在构造函数RelationOf中捕获Relation r a b实例。

data RelationOf a b where
    RelationOf :: Relation r a b => r -> RelationOf a b

personPets可以写得像一样随心所欲

personPets :: Person -> PetAttachment -> Graph -> [Pet]
personPets person att =
  case r of (RelationOf r') -> neighbours person r'
  where
    r :: RelationOf Person Pet
    r = case att of
      AttachOwns -> RelationOf Owns
      AttachDesires -> RelationOf Desires

neighbors person r行上的case需要从RelationOf构造函数获取捕获的Relation r Person Pet实例。存在限定的GADT捕获的实例只能通过与它们的模式匹配来恢复。

等级2类型

要使用等级2类型,您需要添加以下之一

{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE RankNTypes #-}

对于秩为2的类型,我们可以将personPets分为两部分。第一个将计算出r是什么,OwnsDesires,并将其传递给延续传递风格的通用量化函数。

withAttachment :: PetAttachment -> (forall r. Relation r Person Pet => r -> c) -> c
withAttachment AttachOwns    f = f Owns
withAttachment AttachDesires f = f Desires

personPets将调用withAttachment,将部分应用的neighbours作为延续传递给它。

personPets :: Person -> PetAttachment -> Graph -> [Pet]
personPets person att = withAttachment att (neighbours person)

低技术解决方案:

personPets :: Person -> PetAttachment -> Graph -> [Pet]
personPets person att = case att of
    AttachOwns -> neighbours person Owns
    AttachDesires -> neighbours person Desires

相关内容

最新更新