简体   繁体   English

如何对约束类型变量进行约束?约束?

[英]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)

Single Inheritance 单一继承

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 ShowA的超类

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 hh有一个单一的超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 . 你想showa内部Some ,但这个变量是存在性量化的,所以我们不能依赖于该类型的任何知识的a In particular, we have no way of knowing that a is an instance of Show . 特别是,我们无法知道aShow一个实例。

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM