如何通过泛型编程获取值内部所有TypeRep?已实现方案存问题
Absolutely! You can build this typeReps function using Haskell's generic programming tools from the Data.Data library. Let's break down the issues in your current code and walk through correct implementations that meet your requirements.
Issues in Your Current Code
Your implementation has two critical problems causing duplicate TypeReps and incorrect constructor handling:
- Misusing the input value in
fcstr:
Yourfcstrfunction usestypeOf a(the type of the entire input) instead of the type associated with the constructorgbeing processed. This leads to duplicate root-type entries every time a constructor (like the empty list[]in['a']) is handled. - Ignoring the constructor parameter:
You don't use thegparameter infcstr, and sincegisn't constrained toTypeable, you can't get its actual type. This defeats the purpose ofgfoldl, which passes constructors tofcstrto initialize the folding process.
Correct Implementations
The core goal is to collect the TypeRep of the root value, then recursively gather types from all direct and indirect sub-values. Here are two approaches:
First, Define Your Wrapper Type
Let's start with a proper definition for your TypeReps wrapper (and a helper for concatenation):
{-# LANGUAGE DeriveDataTypeable #-} module Example where import Data.Data import Data.Typeable import Data.List (nub) -- For optional deduplication newtype TypeReps a = TypeReps { getTypes :: [TypeRep] } deriving Show (<++>) :: TypeReps (d -> b) -> TypeReps d -> TypeReps b (<++>) (TypeReps xs) (TypeReps ys) = TypeReps (xs ++ ys)
Option 1: Preserve All Nested Types (Including Duplicates)
This implementation strictly follows the recursive structure of your data, including types from nested constructors like empty lists:
typeReps :: (Data a) => a -> TypeReps a typeReps x = TypeReps $ typeOf x : concatMap (getTypes . typeReps) (gmapQ id x)
Testing this matches your first example perfectly:
>>> typeReps (1 :: Int, 'a') TypeReps {getTypes = [(Int,Char),Int,Char]} >>> typeReps ['a'] TypeReps {getTypes = [[Char],Char,[Char]]} -- Includes empty list's [Char] type
Option 2: Deduplicate Types
If you want to remove duplicate TypeReps (like the extra [Char] from the empty list), use nub to filter duplicates:
typeReps :: (Data a) => a -> TypeReps a typeReps x = TypeReps . nub $ typeOf x : concatMap (getTypes . typeReps) (gmapQ id x)
This gives you the clean output you showed for your Foo example:
>>> data Foo = Foo [Char] deriving (Data, Typeable, Show) >>> typeReps (Foo ['a', 'b']) TypeReps {getTypes = [Foo,[Char],Char]}
Option 3: Corrected gfoldl Approach
If you prefer to stick with gfoldl (as in your original code), here's a fixed version that uses constructors correctly:
typeReps :: (Data a) => a -> TypeReps a typeReps x = gfoldl step (\_ -> TypeReps [typeOf x]) x where step :: forall d b. Data d => TypeReps (d -> b) -> d -> TypeReps b step (TypeReps acc) d = TypeReps $ acc ++ getTypes (typeReps d)
Here, fcstr initializes the fold with the root type once, and step recursively adds types from each sub-value to the accumulator.
Why This Works
gmapQ id xextracts all direct sub-values ofx(e.g., the two elements of a tuple, the list inside aFooconstructor).- Recursively calling
typeRepson each sub-value captures nested types down to the deepest elements. - Concatenating these lists gives a flat sequence of all types from the root to the leaves of your data structure.
内容的提问来源于stack exchange,提问作者Damian Nadales

