9

假设我正在编写一个 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现在,我可以通过case删除两者来操作值,VNum甚至VBool以这种方式重新建立我的幻象类型

getNum :: Valunk -> Maybe (Val Num)
getNum (Valunk n@(VNum _)) = Just n
getNum _                   = Nothing

但这只是感觉就像我正在重新实现Typeable机器。不幸的是,GHC 不会让我推导出一个TypeableforVal

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

对于我所有的类型。

4

2 回答 2

1

First of all, you need to store a witness that the quantified type in Valunk is in Typeable:

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

Once you have this, you can just use gcast to achieve what you're asking for:

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

This was tested with:

data Val a where
  VNum  :: Int  -> Val Int
  VBool :: Bool -> Val Bool
于 2013-08-15T05:32:38.483 回答
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 

这将提供获取它的功能。请务必正确命名您的类型(从而如实归档包、模块和类型),否则其他包可能会遇到问题。

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

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

于 2013-08-11T21:28:57.857 回答