从DataKinds中检索信息约束存在类型

时间:2014-06-24 13:25:40

标签: haskell existential-type data-kinds

如果我有一个由有限DataKind

约束的类型
{-# LANGUAGE DataKinds #-}

data K = A | B

data Ty (a :: K) = Ty { ... }

和一种存在类型,它会忘记类型中K的确切选择...但会在传递的字典中记住它。

class AK (t :: K) where k :: Ty t -> K
instance AK A where k _ = A
instance AK B where k _ = B

data ATy where ATy :: AK a => Ty a -> ATy

ATy <-> Either (Ty A) (Ty B)确实如此,但如果我想写一个我需要使用的证人unsafeCoerce

getATy :: ATy -> Either (Ty A) (Ty B)
getATy (ATy it) = case k it of
  A -> Left  (unsafeCoerce it)
  B -> Right (unsafeCoerce it)

所以一般来说这是有用的 - 我可以忘记使用K选择ATy并使用getATy部分记住它。总而言之,这充分利用了我所拥有的类型信息。

但是,如果正确完成,这种类型的信息感觉应该是“显而易见的”。

有没有办法在不使用unsafeCoerce的情况下实现上述目标?有没有办法摆脱存在主义中的AK约束?这种技术是否可以完全基于数据类型提供的信息约束来执行?

1 个答案:

答案 0 :(得分:9)

如果要对存在类型进行运行时案例分析,还需要单例表示:

{-# 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中实例化Ty k;它必须保持多态。

有多种方法可以提供运行时类型信息;隐含地通过词典是一种选择,正如我们所见:

data ATy where
   ATy :: AK k => Ty k -> ATy

此处AK k只是指向SK的指针(因为AK类只有一个方法,我们没有该类的字典,只是一个指向方法的普通指针),构造函数中的一个额外字段。我们也可以选择明确该字段:

data ATy where
    ATy :: SK k -> Ty k -> ATy

和运行时表示几乎相同。

第三种选择是使用GADT构造函数对类型进行编码:

data ATy where
    ATyA :: Ty A -> ATy
    ATyB :: Ty B -> ATy

这个解决方案性能非常好,因为没有空间开销,因为构造函数已经对类型进行了编码。它类似于具有隐藏类型参数的Either