如何为 DataKinds 派生类型自动派生 Typeable 实例?

How can I automatically derive Typeable instance for DataKinds derived types?

我有一些这样的类型:

data Currency = USD | EUR
              deriving (Show, Typeable)

data Money :: Currency -> * where
  Money :: Int -> Money c
  deriving (Show, Typeable)

我想在这个函数中使用 typeOf

findRate :: Money a -> Rates -> Maybe Double
findRate a = M.lookup (typeOf a)

那没有用,因为 findRate 中的类型 a 没有 Typeable 实例。所以我通过这样做来修复它:

deriving instance Typeable USD
deriving instance Typeable EUR
findRate :: (Typeable a) => Money a -> Rates -> Maybe Double

然而,当货币数量增加时,这会变成很多样板。有没有办法指定所有类型的 Currency 都应该派生一个 Typeable 实例?

编辑:此外,一种方法可以推断 Money a 中的 aTypeable 会很好,所以我不需要添加 (Typeable a) =>无处不在。不过那是次要的。

是的,您可以使用 AutoDeriveTypeable 扩展程序。

对于另一部分,我能想到的最接近的事情是将 Typeable c => 放入 GADT 定义中,如下所示:

{-# LANGUAGE AutoDeriveTypeable #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE DataKinds #-}

import Data.Typeable
import qualified Data.Map as M

type Rates = M.Map TypeRep Double

data Currency = USD | EUR
              deriving (Show, Typeable)

data Money :: Currency -> * where
  Money :: Typeable c => Int -> Money c

instance Show (Money c) where
    show (Money n) = "Money " ++ show n

findRate :: Money a -> Rates -> Maybe Double
findRate a@(Money _) = M.lookup (typeOf a)

注意:

  • 根据 GADT 的性质,这需要实际评估 a 以从中获取 Typeable 上下文,而 typeOf 本身不需要。
  • 这似乎破坏了为 GADT 自动派生 Show 的能力。

如果您正在跟踪需要 Typeable 的旧文档,则您可能不需要它。从 ghc 8.2 开始,对 Typeable 的显式调用已被弃用并包含在“par for the course”中;所以不需要语言 pragma 等。