适当地铸造GADT

时间:2013-08-07 17:15:13

标签: haskell existential-type gadt

假设我正在编写DSL并希望支持幻像类型支持和严重类型化的表达式。我的值类型可能是

{-# LANGUAGE GADTs, DataKinds #-}

data Ty = Num | Bool deriving (Typeable)

data Val a where
  VNum  :: Int  -> Val Num
  VBool :: Bool -> Val Bool

我可以使用幻像删除版

{-# LANGUAGE ExistentialQuantification #-}

data Valunk = forall a . Valunk (V' a)

现在,我可以通过Valunk caseVNum VBool操作getNum :: Valunk -> Maybe (Val Num) getNum (Valunk n@(VNum _)) = Just n getNum _ = Nothing 的值,甚至以这种方式重新构建我的幻像类型

Typeable

但这只是感觉我正在重新实现Typeable机制。不幸的是,GHC不允许我为Val

派生src/Types/Core.hs:97:13: Can't make a derived instance of `Typeable (Val a)': Val must only have arguments of kind `*' In the data declaration for Val
getIt :: Typeable a => Valunk -> Maybe (Val a)
getIt (Valunk v) = cast v

有没有办法解决这个限制?我很想写

class Typeably b x where kast :: x a -> Maybe (x b)
instance Typeably Num Val where 
  kast n@(VNum _) = Just n
  kast _          = Nothing

但是现在我不得不诉诸这样的机器

{{1}}

我的所有类型。

2 个答案:

答案 0 :(得分:1)

您可以自己派生Data.Typeable:

{-# LANGUAGE GADTs, DataKinds, DeriveDataTypeable, ExistentialQuantification #-}

import Data.Typeable

data Ty = TNum | TBool deriving Typeable 

data Valunk = forall a. Typeable a => Valunk a 

data Val a where 
    VInt :: Int -> Val TNum
    VBool :: Bool -> Val TBool 

instance Show (Val a) where 
    show (VInt a) = show a
    show (VBool a) = show a 

valtypenam = mkTyCon3 "package" "module" "Val"

instance Typeable (Val a) where 
    typeOf _ = mkTyConApp valtypenam []

getIt :: Valunk -> Maybe (Val a)
getIt (Valunk p) = cast p 

这将提供get it功能。只需确保正确命名您的类型(因此如实提交文件包,模块和类型)否则其他包可能会遇到问题。

有关如何编写这些实例的更多示例,请查看:Data.Derive.Typeable source

编辑:我在代码中有一个非常奇怪的副本和过去的错误,但现在它有效。

答案 1 :(得分:1)

首先,您需要存储Valunk中量化类型位于Typeable的见证人:

data Valunk = forall a . Typeable a => Valunk (Val a)

一旦你有了这个,你可以使用gcast来实现你的要求:

getIt :: Typeable a => Valunk -> Maybe (Val a)
getIt (Valunk v) = gcast v

用以下方法测试:

data Val a where
  VNum  :: Int  -> Val Int
  VBool :: Bool -> Val Bool