如何在Haskell中创建不同类型的对象列表?

时间:2010-09-15 23:49:13

标签: generics list haskell

我有这两种Haskell数据类型:

data Code 
    = Code_A | Code_B | Code C
    deriving (Eq,Show)

data ListObject = Code | Int

我需要制作一个包含ListObjects的列表。这是整数值和代码([1,2, Code_A, 3])。我知道它应该是可能的,但我无法弄清楚它的语法。 Haskell可以做一些简洁的东西,但它的语法很糟糕。一些帮助将不胜感激。

4 个答案:

答案 0 :(得分:12)

怎么样:

data Code 
     = Code_A | Code_B | Code_C
     deriving (Eq,Show)

data ListObject = ListCode Code | Value Int

objects :: [ListObject]
objects = [ListCode Code_A, Value 0]

应该这样做。

答案 1 :(得分:10)

您定义ListObject的方式恰好有两个ListObject类型的值:CodeValue。这有点类似于C语言中的enum ListObject {Code, Int}。类型为Code的costructor ListObject实际上与Code类型无关(Int也是如此)。

所以你定义它的方式,ListObject的列表如下所示:[Int, Code, Code, Int]。当然这不是很有用。

您可能想要做的是这样的事情:data ListObject = CodeObject Code | IntObject Int,其中“ListObject是包含Int的IntObject,或包含代码的CodeObject”。

根据该定义,您的列表可能看起来像[IntObject 42, Code Code_A, IntObject 23]

答案 2 :(得分:7)

当然需要一些术语。在您的代码中,令牌CodeListObject都是“数据类型”,并且位于一个名称空间中。

第二个概念是“构造函数”,用于构造数据类型的特定实例的命令。构造函数位于单独的名称空间中 - 在您的代码中,这些名称空间包括令牌Code_ACode_BCode_CCodeInt

这是你的困惑。您认为您已经说过想要一种名为ListObject的数据类型,其中包含CodeInt的数据类型。相反,您已使用名为ListObjectCode的两个Null(零内容)构造函数定义Int

现在我们找到解决方案,罗伯特在答案中跳了出来:

data ListObject = CodeObject Code | IntObject Int

请注意语法:

data [DataTypeName] = [ConstructorName] [Component Data Type 1] | ...

使用英语编写上述代码,我们有一个数据类型ListObject,其中包含两个构造函数(CodeObjectIntObject),每个构造函数都采用单一数据类型({{1}分别和Code创建Int类型的实例。

如果您理解,请停在这里!有些人使用函数类型签名来构造函数时更容易(并且更难)。使用这种表示法的上述ListObject如下所示:

ListObject

这清楚地表明data ListObject where CodeObject :: Code -> ListObject IntObject :: Int -> ListObject 是接受CodeObject并返回Code的某种功能。与ListObject

类似的故事

答案 3 :(得分:4)

  

Example: heterogeneous lists

     

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中的值应用任何函数,因为它们可能不是Bool。但我们确实知道每个元素:它们可以通过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的类型。但正如我们所提到的,由于Show构造函数的约束,我们知道该类型是SB的实例。因此,使用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
Code_A
3
Code_C
0
"but..."

其他答案显示了您可能想要的方法,但这是为了完整性并提供思考的食物。如SB "but..."位所示,ShowBox更宽松。

向我们提供有关您正在处理的问题的更多信息,我们可以为您提供针对您的具体情况的更多有用的建议。