[英]How can I create a list of objects of different type in Haskell?
I have these two Haskell data types: 我有以下两种Haskell数据类型:
data Code
= Code_A | Code_B | Code C
deriving (Eq,Show)
data ListObject = Code | Int
I need to make a list that contains ListObjects
. 我需要制作一个包含ListObjects
的列表。 That is both integer values and codes ([1,2, Code_A, 3])
. 那既是整数值又是代码([1,2, Code_A, 3])
。 I know it should be possible but I just can't figure the syntax for it. 我知道应该有可能,但是我无法弄清楚它的语法。 Haskell can do some neat stuff but its syntax sucks. Haskell可以做一些巧妙的事情,但是其语法很糟糕。 Some help would be greatly appreciated. 一些帮助将不胜感激。
How about: 怎么样:
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]
That should do it. 那应该做。
The way you defined ListObject
there are exactly two values of type ListObject
: Code
and Value
. 定义ListObject
的方式中,存在ListObject
类型的两个值: Code
和Value
。 This is somewhat similar to enum ListObject {Code, Int}
in C-like languages. 这有点类似于C语言中的enum ListObject {Code, Int}
。 The costructor Code
of type ListObject
doesn't actually have anything to do with the type Code
(same goes for Int
). 该costructor Code
类型ListObject
实际上不会有什么做的类型Code
(同样适用于Int
)。
So the way you defined it, a list of ListObject
s would look like this: [Int, Code, Code, Int]
. 因此,按照定义方式, ListObject
的列表如下所示: [Int, Code, Code, Int]
。 Of course that's not very useful. 当然那不是很有用。
What you probably want to do is something like this: data ListObject = CodeObject Code | IntObject Int
您可能想要做的是这样的: data ListObject = CodeObject Code | IntObject Int
data ListObject = CodeObject Code | IntObject Int
, which says "A ListObject is either an IntObject containing an Int, or a CodeObject containing a Code". data ListObject = CodeObject Code | IntObject Int
,它表示“ ListObject是包含Int的IntObject或包含代码的CodeObject”。
With that definition your list may look like [IntObject 42, Code Code_A, IntObject 23]
. 使用该定义,您的列表可能看起来像[IntObject 42, Code Code_A, IntObject 23]
。
Some terminology is certainly needed. 当然需要一些术语。 In your code the tokens Code
and ListObject
are both "data types" and are in one name space. 在您的代码中,标记Code
和ListObject
都是“数据类型”,并且在一个名称空间中。
A second concept is "Constructors", commands used to construct a particular instance of a data type. 第二个概念是“构造函数”,用于构造数据类型的特定实例的命令。 Constructors live in a separate name space - in your code these include the tokens Code_A
, Code_B
, Code_C
and Code
, Int
. 构造函数位于单独的名称空间中-在您的代码中,这些Code_A
包括标记Code_A
, Code_B
, Code_C
和Code
, Int
。
Here's your confusion. 这是您的困惑。 You think you've said you want a data type called ListObject
which contains data types of either Code
or Int
. 您认为您已经说过想要一个名为ListObject
数据类型,其中包含Code
或Int
数据类型。 When instead you've defined ListObject
with two Null (zero content) constructors called Code
and Int
. 相反,您使用两个名为Code
和Int
Null(零内容)构造函数定义了ListObject
。
Now we get to the solution, which Robert jumped to in his answer: 现在我们找到解决方案,Robert跳到他的回答中:
data ListObject = CodeObject Code | IntObject Int
Notice the syntax: 注意语法:
data [DataTypeName] = [ConstructorName] [Component Data Type 1] | ...
Working out the above code in English, we have a data type ListObject
with two constructors ( CodeObject
and IntObject
), each constructor takes a single data type ( Code
and Int
respectively) to create an instance of the ListObject
type. 用英语编写以上代码,我们有一个数据类型ListObject
带有两个构造函数( CodeObject
和IntObject
),每个构造函数都采用一个数据类型(分别为Code
和Int
)来创建ListObject
类型的实例。
Stop here if you understand! 如果您理解,请在这里停止! Some people have an easier time (and some a harder time) with using function type signatures for constructors. 对于构造函数使用函数类型签名,有些人度过了更轻松的时光(有些日子更艰难)。 The above ListObject
using such notation looks like: 上面使用这种表示法的ListObject
看起来像:
data ListObject where
CodeObject :: Code -> ListObject
IntObject :: Int -> ListObject
This makes it clear that CodeObject
is some sort of function accepting Code
and returning a ListObject
. 这清楚表明CodeObject
是某种接受Code
并返回ListObject
的函数。 Similar story with IntObject
与IntObject
类似的故事
Example: heterogeneous lists 示例:异构列表
The premise behind Haskell's typeclass system is grouping types that all share a common property. Haskell的typeclass系统背后的前提是对所有具有共同属性的类型进行分组。 So if you know a type instantiates some class
C
, you know certain things about that type. 因此,如果您知道类型实例化了某些类C
,那么您就知道有关该类型的某些知识。 For example, Int instantiatesEq
, so we know that elements of Int can be compared for equality. 例如,Int实例化Eq
,因此我们知道可以比较Int的元素是否相等。Suppose we have a group of values, and we don't know if they are all the same type, but we do know they all instantiate some class, ie we know all the values have a certain property. 假设我们有一组值,并且我们不知道它们是否都是同一类型,但是我们知道它们都实例化了某个类, 即我们知道所有值都具有特定的属性。 It might be useful to throw all these values into a list. 将所有这些值放入列表中可能会很有用。 We can't do this normally because lists are homogeneous with respect to types: they can only contain a single type. 我们通常不能这样做,因为列表在类型方面是同质的:它们只能包含一个类型。 However, existential types allow us to loosen this requirement by defining a 'type hider' or 'type box': 但是,存在类型允许我们通过定义“类型隐藏器”或“类型框”来放宽此要求:
Example: Constructing a heterogeneous list 示例:构造一个异构列表
data ShowBox = forall s. Show s => SB s heteroList :: [ShowBox] heteroList = [SB (), SB 5, SB True]
We won't explain precisely what we mean by that datatype definition, but its meaning should be clear to your intuition. 我们不会确切解释该数据类型定义的含义,但是您的直觉应该清楚其含义。 The important thing is that we're calling the constructor on three values of different types, and we place them all into a list, so we must end up with the same type for each one. 重要的是,我们要在三个不同类型的值上调用构造函数,并将它们全部放入一个列表中,因此对于每个值,我们都必须以相同的类型结束。 Essentially this is because our use of the
forall
keyword gives our constructor the typeSB :: forall s. Show s => s -> ShowBox
本质上这是因为我们对forall
关键字的使用使构造函数的类型为SB :: forall s. Show s => s -> ShowBox
SB :: forall s. Show s => s -> ShowBox
.SB :: forall s. Show s => s -> ShowBox
。 If we were now writing a function that we were intending to passheteroList
, we couldn't apply any functions like not to the values inside theSB
because they might not beBool
s. 如果现在正在编写要传递heteroList
的函数,则无法将类似not的任何函数应用于SB
内部的值,因为它们可能不是Bool
。 But we do know something about each of the elements: they can be converted to a string via show. 但是我们确实了解每个元素:可以通过show将它们转换为字符串。 In fact, that's pretty much the only thing we know about them. 实际上,这几乎是我们唯一了解的内容。Example: Using our heterogeneous list 示例:使用我们的异构列表
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
Let's expand on this a bit more. 让我们进一步扩展一下。 In the definition of show for
ShowBox
– the line marked with (*) see the comment in the text below – we don't know the type ofs
. 在ShowBox
的show定义中-用(*)标记的行请参见下面的文本中的注释-我们不知道s
的类型。 But as we mentioned, we do know that the type is an instance ofShow
due to the constraint on theSB
constructor. 但是正如我们提到的,由于SB
构造函数的限制,我们确实知道该类型是Show
的实例。 Therefore, it's legal to use the function show ons
, as seen in the right-hand side of the function definition. 因此,使用在s
显示的函数是合法s
,如函数定义右侧所示。As for
f
, recall the type off
,请记住Example: Types of the functions involved 示例:所涉及功能的类型
print :: Show s => s -> IO () -- print x = putStrLn (show x) mapM_ :: (a -> mb) -> [a] -> m () mapM_ print :: Show s => [s] -> IO ()
As we just declared
ShowBox
an instance ofShow
, we can print the values in the list. 正如我们刚刚将ShowBox
声明为Show
的实例一样,我们可以在列表中打印值。
Applying it to your situation, you get 将其应用于您的情况,您会得到
{-# 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
Output: 输出:
ghci> f Code_A 3 Code_C 0 "but..."
The other answers show the approach you likely want, but this is here for completeness and to offer food for thought. 其他答案显示了您可能想要的方法,但这是出于完整性和思考的目的。 As the SB "but..."
bit suggests, ShowBox
is more permissive. 正如SB "but..."
位所暗示的那样, ShowBox
更宽松。
Give us more information about the problem you're working on, and we can give you more helpful suggestions that are specific to your situation. 向我们提供有关您正在解决的问题的更多信息,并且我们可以为您提供针对您情况的更有用的建议。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.