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

时间:2015-02-24 19:08:21

标签: haskell ghc typeclass gadt

我正在玩GHC的ConstraintKinds扩展名。 我有以下数据类型,这只是一个框,用于完成一个参数约束的事件c

data Some (c :: * -> Constraint) where
    Some :: forall a. c a => a -> Some c

例如,我可以构造一个带有某种数字的盒子(可能不是很有用)。

x :: Some Num
x = Some (1 :: Int)

现在,只要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

但是如何在实例上下文中标记此要求(标有???)?

我不能使用等式约束(c ~ Show),因为两者不一定相等。 c可以是Num,这意味着Show,但不等于{。}}。

修改

我意识到一般来说这是不可能的。

如果您有两个Some Eq类型的值,则无法将它们进行相等性比较。它们可以是不同的类型,每个类型都有自己的平等概念。

适用于Eq的内容适用于第一个函数箭头右侧显示类型参数的任何类型类(如a中的第二个(==) :: a -> a -> Bool)。< / p>

考虑到没有办法创建表达“此类型变量未在第一个箭头之外使用”的约束,我认为不可能编写我想写的实例。

3 个答案:

答案 0 :(得分:10)

我们能够得到的最接近的是一个Class1类,它将类和单个超类约束之间的关系作为一个类进行补充。它基于Classconstraints

首先,我们将简要介绍一下约束包。 Dict会抓取Constraint

的字典
data Dict :: Constraint -> * where
  Dict :: a => Dict a

:-捕获一个约束需要另一个约束。如果我们有a :- b,只要我们有约束a,我们就可以为约束b生成字典。

newtype a :- b = Sub (a => Dict b)

我们需要一个类似于:-的证明,我们需要知道forall a. h a :- b ah a => Dict (b a)

单一继承

实际上,仅使用单一继承为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

我们将定义类k -> Constraint的约束类,其中约束具有单个超类。

class Class1 b h | h -> b where
    cls1 :: h a :- b a

我们现在有能力解决我们的示例问题。我们有一个A课,需要Show个实例。

 class Show a => A a
 instance A Int

ShowA

的超类
instance Class1 Show A where
    cls1 = Sub Dict 

我们想为Show

撰写Some个实例
data Some (c :: * -> Constraint) where
    Some :: c a => a -> Some c

我们可以Show一个Some Show

instance Show (Some Show) where
    showsPrec x (Some a) = showsPrec x a

每当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)

这让我们写

x :: Some A
x = Some (1 :: Int)

main = print x

答案 1 :(得分:2)

新的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

Show实例的主体中,好像有一个

instance forall a. Implies (c a) (Show a)
范围内的

。如果您那时拥有T :: Type并且知道c T,则专用c T => Show T实例的Implies (c T) (Show T)的超类将允许您派生Show T。必须使用Implies而不是直接的forall a. c a => Show a约束。这种不正确的约束就像

instance forall a. c a => Show a

与每个Show实例causing weird breakage重叠。强制通过本来没有用的约束的超类进行间接修复所有问题。

答案 2 :(得分:1)

除了琐碎之外,您无法将Some c设为Show的实例。

您希望在showa Some,但该变量是存在量化的,因此我们不能依赖a类型的任何知识。特别是,我们无法知道aShow的实例。

编辑:我会扩展我的答案。 即使有更多的机器,放弃Show实例,我仍然不认为你想要什么是因为存在量化。

首先,我将以更熟悉的形式重写Some

data Dict p where
    Dict :: p a => a -> Dict p

谈论“约束暗示约束”的常用方法是约束蕴涵的概念。

data p :- q where
    Sub :: p a => Dict q -> p :- q

我们可以考虑p :- q类型的值作为证据,如果约束forall a. p a成立,那么forall a. q a就会出现。

现在我们尝试编写一个合理的show - ish函数

showD :: p :- Show -> Dict p -> String
showD (Sub (Dict a)) (Dict b) = show b

乍一看,这可能会奏效。我们将以下约束纳入范围(原谅伪 - exists语法)

(0) p :: * -> Constraint
(1) exists a. p a           -- (Dict p)
(2) exists b. p b => Show b -- (p :- Show)

但现在情况崩溃了,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   

因为无法将来自a的{​​{1}}与来自(1)的{​​{1}}统一起来。

这与评论中提到的b包中使用的基本思想相同。