如果你想对存在类型进行运行时案例分析,你也需要一个单例表示:
{-# LANGUAGE DataKinds, GADTs, KindSignatures, ScopedTypeVariables #-}
data K = A | B
-- runtime version of K.
data SK (k :: K) where
SA :: SK A
SB :: SK B
-- ScopedTypeVariables makes it easy to specify which "k" we want.
class AK (k :: K) where
k :: SK k
instance AK A where k = SA
instance AK B where k = SB
data Ty (a :: K) = Ty
data ATy where
ATy :: AK k => Ty k -> ATy
getATy :: ATy -> Either (Ty A) (Ty B)
getATy (ATy (ty :: Ty k)) = case (k :: SK k) of
SA -> Left ty
SB -> Right ty
这个singletons
包可以在这里用来取消样板:
{-# LANGUAGE DataKinds, GADTs, TypeFamilies, TemplateHaskell, ScopedTypeVariables #-}
import Data.Singletons.TH
$(singletons [d| data K = A | B |])
data Ty (a :: K) = Ty
data ATy where
ATy :: SingI k => Ty k -> ATy
getATy :: ATy -> Either (Ty A) (Ty B)
getATy (ATy (ty :: Ty k)) = case (sing :: Sing k) of
SA -> Left ty
SB -> Right ty
至于你的最后一个问题:
有没有办法摆脱存在主义中的 AK 约束?这种技术是否可以完全基于数据类型提供的信息约束来执行?
只要我们只有一个数据类型作为类型参数,信息不存在运行时,我们无法对其进行任何分析。例如,采用这种类型:
data ATy where
ATy :: Ty k -> ATy
我们永远无法实例化k
in Ty k
;它必须保持多态性。
有多种方法可以提供运行时类型信息;正如我们所见,隐式传递的字典是一种选择:
data ATy where
ATy :: AK k => Ty k -> ATy
这里AK k
只是一个指向 an 的指针SK
(因为AK
该类只有一个方法,我们没有该类的字典,只有一个指向该方法的普通指针),构造函数中的一个额外字段。我们还可以选择使该字段显式:
data ATy where
ATy :: SK k -> Ty k -> ATy
并且运行时表示几乎相同。
第三种选择是使用 GADT 构造函数对类型进行编码:
data ATy where
ATyA :: Ty A -> ATy
ATyB :: Ty B -> ATy
这个解决方案在性能方面非常好,因为没有空间开销,因为构造函数已经对类型进行了编码。这就像一个Either
带有隐藏类型参数的。