[英]How to put constraints on type variable of kind `Constraint`?
I'm playing around with the ConstraintKinds
extension of GHC. 我正在玩GHC的ConstraintKinds
扩展。 I have the following data type, which is just a box for things fulfilling some one parameter constraint c
: 我有以下数据类型,这只是一个实现某个参数约束c
的框:
data Some (c :: * -> Constraint) where
Some :: forall a. c a => a -> Some c
For example, I could construct a box with some kind of number (arguably not very useful). 例如,我可以构造一个带有某种数字的盒子(可能不是很有用)。
x :: Some Num
x = Some (1 :: Int)
Now, as long as c
includes the constraint Show
, I could provide an instance of Show (Some c)
. 现在,只要c
包含约束Show
,我就可以提供Show (Some c)
的实例。
instance ??? => Show (Some c) where
show (Some x) = show x -- Show dictionary for type of x should be in scope here
But how do I express this requirement in the instance context (marked with ???
)? 但是我如何在实例上下文中标记这个要求(用???
标记)?
I cannot use an equality constraint ( c ~ Show
), because the two are not necessarily equal. 我不能使用等式约束( c ~ Show
),因为两者不一定相等。 c
could be Num
, which implies, but is not equal to, Show
. c
可以是Num
,暗示但不等于Show
。
Edit 编辑
I realised that this cannot be possible in general. 我意识到一般来说这是不可能的。
If you have two values of type Some Eq
, it is not possible to compare them for equality. 如果您有两个类型为Some Eq
值,则无法将它们进行相等性比较。 They could be of different types that each have their own notion of equality. 它们可以是不同的类型,每个类型都有自己的平等概念。
What applies to Eq
applies to any type class in which the type parameter appears on the right hand side of the first function arrow (like the second a
in (==) :: a -> a -> Bool
). 什么适用于Eq
适用于任何类型参数,其中类型参数出现在第一个函数箭头的右侧(如第二个a
in (==) :: a -> a -> Bool
)。
Considering that there is no way to create a constraint expressing "this type variable is not used beyond the first arrow", I don't think it is possible to write the instance I want to write. 考虑到没有办法创建表示“此类型变量未在第一个箭头之外使用”的约束,我认为不可能编写我想写的实例。
The closest we are able to get is a Class1
class that reifys the relationship between a class and a single superclass constraint as a class. 我们能够得到的最接近的是一个Class1
类,它将类和单个超类约束之间的关系重新作为一个类。 It's based on the Class
from constraints . 它基于来自约束的Class
。
First, we'll take a short tour of the constraints package. 首先,我们将简要介绍一下约束包。 A Dict
captures the dictionary for a Constraint
Dict
捕获Constraint
的字典
data Dict :: Constraint -> * where
Dict :: a => Dict a
:-
captures that one constraint entails another. :-
捕获一个约束需要另一个约束。 If we have a :- b
, whenever we have the constraint a
we can produce the dictionary for the constraint b
. 如果我们有a :- b
,每当我们有约束a
我们就可以为约束b
生成字典。
newtype a :- b = Sub (a => Dict b)
We need a proof similar to :-
, we need to know that forall a. ha :- ba
我们需要一个类似的证据:-
,我们需要知道forall a. ha :- ba
forall a. ha :- ba
, or ha => Dict (ba)
. forall a. ha :- ba
,或ha => Dict (ba)
。
Actually implementing this for class
es with just single inheritance requires the kitchen sink of language extensions, including OverlappingInstances
. 实际上,只使用单一继承为class
es实现此功能需要使用语言扩展的厨房接收器,包括OverlappingInstances
。
{-# LANGUAGE GADTs #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE OverlappingInstances #-}
import Data.Constraint
We'll define the class of constraints of kind k -> Constraint
where the constraint has a single superclass. 我们将定义类型k -> Constraint
的约束类,其中约束具有单个超类。
class Class1 b h | h -> b where
cls1 :: h a :- b a
We're now equipped to tackle our example problem. 我们现在有能力解决我们的示例问题。 We have a class A
that requires a Show
instance. 我们有一个需要Show
实例的A
类。
class Show a => A a
instance A Int
Show
is a superclass of A
Show
是A
的超类
instance Class1 Show A where
cls1 = Sub Dict
We want to write Show
instances for Some
我们想为Some
编写Show
实例
data Some (c :: * -> Constraint) where
Some :: c a => a -> Some c
We can Show
a Some Show
. 我们可以Show
Some Show
。
instance Show (Some Show) where
showsPrec x (Some a) = showsPrec x a
We can Show
a Some h
whenever h
has a single superclass b
and we could show Some b
. 我们可以Show
一个Some h
时h
有一个单一的超b
,我们可以展示Some b
。
instance (Show (Some b), Class1 b h) => Show (Some h) where
showsPrec x (Some (a :: a)) =
case cls1 :: h a :- b a of
Sub Dict -> showsPrec x ((Some a) :: Some b)
This lets us write 这让我们写
x :: Some A
x = Some (1 :: Int)
main = print x
The new QuantifiedConstraints
extension allows this. 新的QuantifiedConstraints
扩展允许这样做。
class (a => b) => Implies a b where
instance (a => b) => Implies a b where
instance (forall a. c a `Implies` Show a) => Show (Some c) where
show (Some x) = show x
Within the body of the Show
instance, it is as if there is a 在Show
实体的主体内,就好像有一个
instance forall a. Implies (c a) (Show a)
in scope. 在适用范围。 If you then have T :: Type
and know c T
, then the superclass of c T => Show T
of the specialized Implies (c T) (Show T)
instance allows you to derive Show T
. 如果你有T :: Type
并且知道c T
,那么c T => Show T
的超类的专用Implies (c T) (Show T)
实例允许你派生Show T
It is necessary to use Implies
instead of a straight forall a. ca => Show a
有必要使用Implies
而不是直接的forall a. ca => Show a
forall a. ca => Show a
constraint. forall a. ca => Show a
约束。 This incorrect constraint acts like 这种不正确的约束就像
instance forall a. c a => Show a
which overlaps with every Show
instance, causing weird breakage . 与每个Show
实例重叠, 导致奇怪的破损 。 Forcing an indirection through the superclass of an otherwise useless constraint fixes everything. 强制间接通过其他无用约束的超类来修复所有内容。
You cannot make Some c
an instance of Show
, except trivially. 除了琐碎之外,你不能使Some c
成为Show
的实例。
You want to show
the a
inside Some
, but that variable is existentially quantified, so we cannot depend on any knowledge of the type of a
. 你想show
的a
内部Some
,但这个变量是存在性量化的,所以我们不能依赖于该类型的任何知识的a
。 In particular, we have no way of knowing that a
is an instance of Show
. 特别是,我们无法知道a
是Show
一个实例。
EDIT: I'll expand on my answer. 编辑:我会扩展我的答案。 Even with more machinery, and giving up on a Show
instance, I still don't think what you want is possible because of the existential quantification. 即使有了更多的机器,放弃了Show
实例,由于存在量化,我仍然不认为你想要的是什么。
First I'll rewrite Some
in a more familiar form 首先,我将以更熟悉的形式重写Some
data Dict p where
Dict :: p a => a -> Dict p
The usual way to talk about "constraints implying constraints" is with the concept of constraint entailment. 谈论“约束暗示约束”的通常方式是约束蕴涵的概念。
data p :- q where
Sub :: p a => Dict q -> p :- q
We can think about a value of type p :- q
as a proof that if the constraint forall a. pa
我们可以考虑类型p :- q
的值作为证明,如果约束条件为forall a. pa
forall a. pa
holds, then forall a. qa
forall a. pa
持有,然后forall a. qa
forall a. qa
follows. forall a. qa
如下。
Now we try to write a sensible show
-ish function 现在我们尝试编写一个合理的show
-ish函数
showD :: p :- Show -> Dict p -> String
showD (Sub (Dict a)) (Dict b) = show b
At a glance, this might work. 乍一看,这可能会奏效。 We have brought the following constraints into scope (forgive the pseudo- exists
syntax) 我们将以下约束纳入范围(原谅伪exists
语法)
(0) p :: * -> Constraint
(1) exists a. p a -- (Dict p)
(2) exists b. p b => Show b -- (p :- Show)
But now things fall apart, GHC rightfully complains: 但现在情况崩溃了,GHC理所当然地抱怨道:
main.hs:10:33:
Could not deduce (Show a2) arising from a use of `show'
from the context (p a)
bound by a pattern with constructor
Sub :: forall (p :: * -> Constraint) (q :: * -> Constraint) a.
(p a) =>
Dict q -> p :- q,
in an equation for `showD'
at main.hs:10:8-19
or from (Show a1)
bound by a pattern with constructor
Dict :: forall (p :: * -> Constraint) a. (p a) => a -> Dict p,
in an equation for `showD'
at main.hs:10:13-18
or from (p a2)
bound by a pattern with constructor
Dict :: forall (p :: * -> Constraint) a. (p a) => a -> Dict p,
in an equation for `showD'
at main.hs:10:23-28
because it is impossible to unify the a
from (1)
with the b
from (2)
. 因为不可能统一a
从(1)
与b
从(2)
This is the same essential idea that is used throughout the constraints
package mentioned in the comments. 这与评论中提到的constraints
包中使用的基本思想相同。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.