我正在玩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>
考虑到没有办法创建表达“此类型变量未在第一个箭头之外使用”的约束,我认为不可能编写我想写的实例。
答案 0 :(得分:10)
我们能够得到的最接近的是一个Class1
类,它将类和单个超类约束之间的关系作为一个类进行补充。它基于Class
的constraints。
首先,我们将简要介绍一下约束包。 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 a
或h 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
Show
是A
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
的实例。
您希望在show
内a
Some
,但该变量是存在量化的,因此我们不能依赖a
类型的任何知识。特别是,我们无法知道a
是Show
的实例。
编辑:我会扩展我的答案。
即使有更多的机器,放弃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
包中使用的基本思想相同。