在我正在处理的HLearn库中,我有一些容器数据类型如下所示:
data (Model params model) => Container' params model = Container'
{ baseparams :: params
, basemodel :: model
}
问题是这种类型使用起来很难,因为params
和model
都是唯一确定的:
class Model params model | params -> model, model -> params
因此,如果我在指定类型时不必指定它们,那将会更方便。编译器应能够自动为我完成。
我解决这个问题的想法是创建一个使用存在量化的类型别名:
type Container model = forall params . (Model params model) => Container' params model
但这不起作用。如果我像往常一样制作Container'
实例,一切正常:
data ContainerParams params = ContainerParams params
instance (Model params model) => Model (ContainerParams params) (Container' params model)
但是当我使用Container
类型时:
instance (Model params model) => Model (ContainerParams params) (Container model)
ghc爆炸:
非法多态或限定类型:容器模型 在“Model(ContainerParams params)(容器模型)”的实例声明中
我不知道此错误消息的含义。是否有可能以某种方式修复我的解决方案以创建Container
类型,您不必指定参数?
编辑:我应该注意,将forall
语句移动到Container'
声明似乎需要一堆unsafeCoerce
s,所以这似乎是一个糟糕的解决方案。
此外,我可以将type Container
更改为data Container
并让事情发挥作用,但这需要我重新声明Conatiner'
所属的所有实例,而我不想去做。我有许多不同的类型遵循这种模式,因此似乎应该有一种通用的方法来解决这个问题。
答案 0 :(得分:12)
我不确定您是否需要通用或存在量化。无论哪种方式,最好将其包装成新鲜的类型。
强烈建议:不要在普通数据类型上使用约束头。他们会让你的生活更加艰难,而不是更容易。他们没有任何用处。
{-# LANGUAGE GADTs #-}
data Container' params model = Container'
{ baseparams :: params
, basemodel :: model
}
data Container p m where
Container :: Model params model => Container' params model -> Container params model
{-# LANGUAGE Rank2Types #-}
data Container' params model = Container'
{ baseparams :: params
, basemodel :: model
}
newtype Container model = Container (forall params . Model params model => Container' params model)
您不能在类型类实例中使用通用或限定类型。所以
instance Model (ContainerParams params) (Container model)
由于类型同义词扩展为,因此不允许
instance Model (ContainerParams params) (forall ...)
在我的GADT解决方案中,我将param
和model
都视为参数。这是因为需要知道的重要事项:功能依赖性不会融合!并且,编译器不会假定它们为了类型检查而汇合。函数依赖性仅用于指导约束求解器(在某种程度上它们类似于prolog中的“cut”之类的额外逻辑构造)。如果您想要融合,请使用TypeFamilies
。
class Model model where
type Param model
...
或者这样做的好方法
class (model ~ (TheModel param),param ~ (TheParam model)) => Model model param where
type TheModel param
type TheParam model
与fundep具有相同的双向性。然后,这会让你把你的存在实例写成
data Container model where
Container :: Model model param => Container' model param -> Container model
然后你就可以把两个容器组合成相同的model
类型,知道存在量化的params
会匹配。使用它,您可以定义
data HasParam model where
HasParam :: Model model param => HasParam model
data GADTContainer model where
GADTContainer :: Model model param => Container' model param -> GADTContainer model
newtype NewContainer model
= NewContainer (forall param. Model model param => Container' model param)
然后元组(HasParam model, NewContainer model)
可证明与GADTContainer model
同构,这也解释了这些类型之间的关系
无论如何,一旦你有了这个,你可以使用适当的包装类型定义你的实例。