知道:
a
是善良的K
- 唯一的种类
K
是A
和B
Show (Proxy A)
持有
Show (Proxy B)
持有
足以证明Show (Proxy a)
成立。但是类型类不仅是我们需要证明其真实性才能与我们的类型一起使用的命题,它还提供了实现。要实际使用show (x :: Proxy a)
,我们不仅需要证明Show (Proxy a)
存在的实现,我们还需要实际知道它是哪一个。
Haskell 类型变量(无约束)是参数化的:没有办法在 中完全多态a
,也无法检查a
为A
和提供不同的行为B
。您想要的“不同行为”与实际上可能没有参数化的情况一样“接近参数化”,因为当您知道每种类型都有一个实例时,它只是为每种类型选择不同的实例。但这仍然是违反合同的东西的test :: forall (a :: K). Proxy a -> String
意思。
类型类约束允许您的代码在受约束的类型变量中是非参数的,因为您可以使用类型类从类型到实现的映射来根据调用它的类型切换代码的行为方式。所以test :: forall (a :: K). Show (Proxy a) => Proxy a -> String
有效。(就实际实现而言,选择类型的同一个最终调用者a
还为Show (Proxy a)
从您的函数生成的代码提供实例以供使用)。
你可以使用你的instance Show (Proxy (a :: K))
想法;现在您的类型参数函数仍然a :: K
可以使用show (x :: Proxy a)
,因为有一个实例适用于所有a :: K
. 但是实例本身也遇到了同样的问题;in 实例的实现show
是参数 in a
,因此无论如何都不能检查它,以便根据类型返回不同的字符串。
您可以使用 dfeuer's answer 之类的东西,其中Kish
利用约束类型变量的非参数性基本上允许您在运行时检查类型;Kish a
为满足约束而传递的实例字典基本上是为变量选择了哪种类型的运行时记录a
(以迂回的方式)。进一步推动这个想法会让你一路走来Typeable
。但是您仍然需要某种约束来使您的代码在a
.
A
您还可以使用明确表示or选择的运行时表示的类型B
(与 相反Proxy
,它在运行时是一个空值,仅提供选择的编译时表示),例如:
{-# LANGUAGE DataKinds, GADTs, KindSignatures, StandaloneDeriving #-}
data K = A | B
data KProxy (a :: K)
where KA :: KProxy A
KB :: KProxy B
deriving instance Show (KProxy a)
test :: KProxy a -> String
test = show
(注意这里我不仅可以实现Show (Kproxy a)
,我实际上可以派生它,尽管它确实需要独立派生)
这是使用 GADTKProxy
来允许test
in 中的非参数化a
,本质上与 dfeuer 的答案中的约束执行相同的工作,Kish
但避免了向类型签名添加额外约束的需要。在这篇文章的早期版本中,我说test
能够做到这一点,同时保持“只是”参数a
,这是不正确的。
当然,现在要传递代理,您必须实际编写KA
or KB
。在您必须编写Proxy :: Proxy A
以实际选择类型的情况下,这并不麻烦(代理通常就是这种情况,因为您通常只使用它们来修复否则会模棱两可的类型)。但是无论如何,如果你与调用的其余部分不一致,编译器会捕捉到你,但你不能只写一个符号Proxy
,让编译器推断出正确的含义。您可以使用类型类来解决这个问题:
class KProxyable (a :: K)
where kproxy :: KProxy a
instance KProxyable A
where kproxy = KA
instance KProxyable B
where kproxy = KB
然后你可以使用KA
而不是Proxy :: Proxy A
,而kproxy
不是让编译器推断出裸的类型Proxy
。愚蠢的例子时间:
foo :: KProxy a -> KProxy a -> String
foo kx ky = show kx ++ " " ++ show ky
GHCI:
λ foo KA kproxy
"KA KA"
请注意,我实际上并不需要在KProxyable
任何地方设置约束;我kproxy
在类型已知的地方使用。不过,这仍然必须“从顶部进入”(就像满足您的Show (Proxy a)
约束的实例字典一样);没有办法让类型中的函数参数自己a :: K
提出相关KProxy a
的。
因为构造函数和类型之间的对应关系使这项工作有效,所以我不相信您可以像使用 empty-at-runtime 那样制作这种风格的通用代理Proxy
。TemplateHaskell 当然可以为你生成这样的代理类型;我认为单例的概念是这里的一般概念,因此https://hackage.haskell.org/package/singletons可能提供了您需要的东西,但我不太熟悉如何实际使用该包。