Haskell 类型类系统背后的前提是对所有共享一个公共属性的类型进行分组。因此,如果您知道一个类型实例化了某个类C,那么您就知道该类型的某些事情。例如,Int 实例化了Eq,因此我们知道可以比较 Int 的元素是否相等。
假设我们有一组值,我们不知道它们是否都是同一类型,但我们知道它们都实例化了某个类,即我们知道所有值都有一个一定的财产。将所有这些值放入列表中可能很有用。我们通常不能这样做,因为列表在类型方面是同质的:它们只能包含一个类型。但是,存在类型允许我们通过定义“类型隐藏器”或“类型框”来放松这一要求:
示例:构造异构列表
data ShowBox = forall s. Show s => SB s
heteroList :: [ShowBox]
heteroList = [SB (), SB 5, SB True]
我们不会准确解释该数据类型定义的含义,但您的直觉应该很清楚它的含义。重要的是,我们在三个不同类型的值上调用构造函数,并将它们全部放入一个列表中,因此我们必须为每个值都使用相同的类型。本质上这是因为我们使用forall 关键字为我们的构造函数提供了SB :: forall s. Show s => s -> ShowBox 类型。如果我们现在正在编写一个打算传递heteroList 的函数,我们不能将任何函数应用到SB 中的值,因为它们可能不是Bools。但是我们确实知道一些关于每个元素的信息:它们可以通过 show 转换为字符串。事实上,这几乎是我们对它们的唯一了解。
示例:使用我们的异构列表
instance Show ShowBox where
show (SB s) = show s -- (*) see the comment in the text below
f :: [ShowBox] -> IO ()
f xs = mapM_ print xs
main = f heteroList
让我们进一步扩展一下。在ShowBox 的show 定义中——标有(*) 的行见下文中的注释——我们不知道s 的类型。但正如我们所提到的,由于SB 构造函数的约束,我们确实知道该类型是Show 的实例。因此,在s 上使用函数 show 是合法的,如函数定义的右侧所示。
至于f,回忆一下print的类型:
示例:涉及的函数类型
print :: Show s => s -> IO () -- print x = putStrLn (show x)
mapM_ :: (a -> m b) -> [a] -> m ()
mapM_ print :: Show s => [s] -> IO ()
由于我们刚刚将 ShowBox 声明为 Show 的实例,因此我们可以打印列表中的值。
将其应用于您的情况,您会得到
{-# LANGUAGE ExistentialQuantification #-}
data ShowBox = forall a. Show a => SB a
instance Show ShowBox where
show (SB s) = show s
data Code = Code_A | Code_B | Code_C
deriving (Eq,Show)
l :: [ShowBox]
l = [SB Code_A, SB 3, SB Code_C, SB 0, SB "but..."]
f = mapM_ print l
输出:
ghci> f
代码_A
3
代码_C
0
“但是……”
其他答案显示了您可能想要的方法,但这是为了完整性并提供思考的食物。正如SB "but..." 位所暗示的那样,ShowBox 更宽松。
向我们提供有关您正在解决的问题的更多信息,我们可以针对您的具体情况为您提供更多有用的建议。