首页 > 解决方案 > 使用泛型编程获取一个值中的所有 TypeRep

问题描述

有没有办法TypeRep使用泛型编程获取一个值内所有 ' 的列表?

例如,是否可以定义一个函数:

typeReps :: (Data a, Typeable a) => a -> [TypeRep]

以这样的方式:

>>> typeReps (1 :: Int, 'a')
[(Int, Char), Int, Char]

>>> typeReps (Foo ['a', 'b'])
[Foo, [Char], Char, Char]

我试过了

{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE RankNTypes #-}

module Example where

import Data.Data
import Data.Typeable

typeReps :: (Data a, Typeable a) => a -> TypeReps a
typeReps a = gfoldl step fcstr a
  where
    step :: forall d b. (Typeable d, Data d) =>  TypeReps (d -> b) -> d -> TypeReps b
    step tot d = tot <++> typeReps d

    fcstr :: forall g . g -> TypeReps g
    fcstr g  = TypeReps [typeOf a]

但是,这似乎与TypeRep结果中的 type 重复:

>>> typeReps ['a']
TypeReps {getTypes = [[Char],Char,[Char]]}

此外,我没有使用gafsctr上面的函数中看起来有点倒退(我不能,因为我不能限制gTypeable)。

我不知道这种方式能不能解决,如果不能,我想知道是否有其他方法可以接近它。

标签: haskellgeneric-programming

解决方案


正如评论中所建议的那样,您似乎没有考虑到[1,2,3]实际情况1 : 2 : 3 : []每个尾部子项都有 type [Int])。您可以为列表添加一个特殊情况:

{-# LANGUAGE ViewPatterns #-}

import Data.Data

-- | Returns 'Just' only for lists
--
-- This can surely be done more efficiently, but it does the job.
listTypeReps :: Data a => a -> Maybe [TypeRep]
listTypeReps x

  | typeRepTyCon (typeOf x) == listTyCon
  , toConstr x == toConstr ([] :: [()])   -- empty list
  = Just []

  | typeRepTyCon (typeOf x) == listTyCon
  , toConstr x == toConstr [()]           -- cons
  , [headTs, _] <- gmapQ typeReps x
  , [_, Just tailTs] <- gmapQ listTypeReps x
  = Just (headTs ++ tailTs)

  | otherwise
  = Nothing

listTyCon :: TyCon
listTyCon = typeRepTyCon (typeOf ([] :: [()]))

-- | Get the types of subterms
typeReps :: Data a => a -> [TypeRep]
typeReps x = typeOf x : case listTypeReps x of
                          Just ts -> ts
                          Nothing -> concat (gmapQ typeReps x) 

试试看:

ghci> :set -XDeriveDataTypeable
ghci> data Foo = Foo [Int] (Char,Char) deriving (Data,Typeable)
ghci> typeReps $ Foo [1, 2] ('a', 'b')
[Foo,[Int],Int,Int,(Char,Char),Char,Char]

推荐阅读