我有一个类C
,其中包含一种类型和元组的实例。
class C a
instance C Int
instance (C a, C b) => C (a, b)
使用普通Dict
GADT捕获约束
{-# LANGUAGE GADTs #-}
{-# LANGUAGE ConstraintKinds #-}
data Dict c where
Dict :: c => Dict c
可以从C a
证明C (a, b)
吗?
fstDict :: Dict (C (a, b)) -> Dict (C a)
fstDict Dict = ???
我怀疑答案是"不是"因为fstDict Dict = Dict
不够,而且其他可能性很少。有没有办法改变C
,以便可以从产品约束中恢复产品组件的约束?
我,或许是错误的,试图完成与the most closely related question相同的事情,但是我可以从类别的一端或两端请求Dict
。
data DSL a b where
DSL :: (Dict C a -> DSL' a b) -> DSL a b
data DSL' a b where
DSL' :: (C a, C b) => ... -> DSL' a b
答案 0 :(得分:1)
一种方法是将所有祖先词典存储在Dict
类型中:
data CDict a where
IntDict :: C Int => CDict Int
PairDict :: C (a, b) => CDict a -> CDict b -> CDict (a, b)
fstCDict :: CDict (a, b) -> CDict a
fstCDict (PairDict fst snd) = fst
这有一个缺点,你必须使CDict
类型反映你的实例的结构。
答案 1 :(得分:1)
Daniel Wagner's answer的开放变体将使用TypeFamily
让实现该类的每个类型指定它所需的上下文。
{-# LANGUAGE GADTs #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ScopedTypeVariables #-}
import GHC.Exts (Constraint)
import Data.Proxy
data Dict c where
Dict :: c => Dict c
该类允许每种类型指定类型所需的附加约束Ctx a
。 cdict
函数会强制关注来自C
的上下文,并提供一种方法来获取基础Ctx
,而不将其包括在Ctx
中,例如class C a where
type Ctx a :: Constraint
cdict :: Proxy a -> CDict a
。产品
CDict
Dict
是一个C a
,它同时包含约束Ctx a
以及类型a
需要的其他上下文type CDict a = Dict (C a, Ctx a)
Int
instance C Int where
type Ctx Int = ()
cdict _ = Dict
实例不需要任何额外的上下文
C a
元组实例需要C b
和instance (C a, C b) => C (a, b) where
type Ctx (a, b) = (C a, C b)
cdict _ = Dict
fstCDict
我们可以为元组编写fstCDict :: forall a b. CDict (a, b) -> CDict a
fstCDict Dict = case cdict (Proxy :: Proxy a) of Dict -> Dict
。
C
如果我们尝试编写一个错误的Show
实例,它会神奇地召唤instance (C a) => C (Maybe a) where
type Ctx (Maybe a) = (C a, Show a)
cdict _ = Dict
个实例
Could not deduce (Show a) arising from a use of `Dict'
from the context (C a)
bound by the instance declaration ...
Possible fix:
add (Show a) to the context of the instance declaration
In the expression: Dict
In an equation for `cdict': cdict _ = Dict
In the instance declaration for `C (Maybe a)'
导致编译器错误
{{1}}