为有些人为的例子道歉。我试图在不失去理由的情况下简化这一点:
假设我有一个多参数类型类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
答案 0 :(得分:4)
以下是两种编写方法,一种使用GADT,另一种使用Rank 2 Type。
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捕获的实例只能通过与它们进行模式匹配来恢复。
要使用Rank 2 Type,您需要添加以下内容之一
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE RankNTypes #-}
使用Rank 2 Type,我们可以将personPets
分成两部分。第一个将确定r
是Owns
还是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