存在性类型歧义

时间:2014-09-22 22:19:08

标签: haskell existential-type

为有些人为的例子道歉。我试图在不失去理由的情况下简化这一点:

假设我有一个多参数类型类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含糊不清:

  

relation.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

2 个答案:

答案 0 :(得分:4)

以下是两种编写方法,一种使用GADT,另一种使用Rank 2 Type。

GADT

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

{-# LANGUAGE GADTs #-}
由于构造函数上的RelationOf a b约束,

Relation r 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

case行的neighbors person r是从Relation r Person Pet构造函数中获取捕获的RelationOf实例所必需的。由存在性合格的GADT捕获的实例只能通过与它们进行模式匹配来恢复。

等级2类型

要使用Rank 2 Type,您需要添加以下内容之一

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

使用Rank 2 Type,我们可以将personPets分成两部分。第一个将确定rOwns还是Desires,并将其传递给continuation passing style中的通用量化函数。

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)

答案 1 :(得分:2)

低技术解决方案:

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