如何构建包含不同MyTypes类型的Test GADT实例的异构列表?
Nice question! The issue you're hitting is that Test 'MyInt and Test 'MyDouble are distinct types—Haskell's standard lists require all elements to be the same type, so you can't just stuff them into [Test t] directly.
The go-to solution here is to use existential types to create a wrapper that erases the specific MyTypes tag, allowing you to collect different Test values into a single list. Let's break this down step by step:
First, keep your original GADT definitions (we'll build on them with the same GADTs extension you're already using):
{-# LANGUAGE GADTs #-} data MyTypes = MyInt | MyDouble data Test (t :: MyTypes) where A :: Int -> Test 'MyInt B :: Double -> Test 'MyDouble
Next, define an existential wrapper GADT that hides the specific t parameter. This wrapper acts as a common type for any Test value, regardless of its MyTypes tag:
data AnyTest where WrapTest :: Test t -> AnyTest
Now you can create your heterogeneous list with no issues:
mixedTestList :: [AnyTest] mixedTestList = [WrapTest (A 123), WrapTest (B 45.67)]
When you want to work with elements from this list, you'll need to pattern match on the wrapper and handle each possible Test constructor. Since we've erased the type tag, the compiler will enforce that you cover all cases to avoid runtime errors:
-- Example: Convert each Test value to a human-readable string showTest :: AnyTest -> String showTest (WrapTest (A intVal)) = "Integer: " ++ show intVal showTest (WrapTest (B doubleVal)) = "Double: " ++ show doubleVal -- Process the entire list at once showAllTests :: [AnyTest] -> [String] showAllTests = map showTest
If you need a more uniform processing step (like converting everything to a common type), define a helper function for Test first, then apply it to the wrapped values:
-- Convert any Test value to a Double testToDouble :: Test t -> Double testToDouble (A n) = fromIntegral n testToDouble (B d) = d -- Apply this conversion to the entire list listToDoubles :: [AnyTest] -> [Double] listToDoubles = map (\(WrapTest t) -> testToDouble t)
As an alternative syntax, you could use the ExistentialQuantification extension instead of a GADT for the wrapper. This is an older approach but works identically:
{-# LANGUAGE ExistentialQuantification #-} data AnyTest = forall t. AnyTest (Test t)
Either way, the core idea is the same: we create a single type (AnyTest) that can hold any Test value by hiding its specific type parameter. This lets you use standard list operations while still being able to unpack and handle each value's original type when needed.
内容的提问来源于stack exchange,提问作者marcosh

