我有这两种Haskell数据类型:
data Code
= Code_A | Code_B | Code C
deriving (Eq,Show)
data ListObject = Code | Int
我需要制作一个包含ListObjects
的列表。这是整数值和代码([1,2, Code_A, 3])
。我知道它应该是可能的,但我无法弄清楚它的语法。 Haskell可以做一些简洁的东西,但它的语法很糟糕。一些帮助将不胜感激。
答案 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
类型的值:Code
和Value
。这有点类似于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)
当然需要一些术语。在您的代码中,令牌Code
和ListObject
都是“数据类型”,并且位于一个名称空间中。
第二个概念是“构造函数”,用于构造数据类型的特定实例的命令。构造函数位于单独的名称空间中 - 在您的代码中,这些名称空间包括令牌Code_A
,Code_B
,Code_C
和Code
,Int
。
这是你的困惑。您认为您已经说过想要一种名为ListObject
的数据类型,其中包含Code
或Int
的数据类型。相反,您已使用名为ListObject
和Code
的两个Null(零内容)构造函数定义Int
。
现在我们找到解决方案,罗伯特在答案中跳了出来:
data ListObject = CodeObject Code | IntObject Int
请注意语法:
data [DataTypeName] = [ConstructorName] [Component Data Type 1] | ...
使用英语编写上述代码,我们有一个数据类型ListObject
,其中包含两个构造函数(CodeObject
和IntObject
),每个构造函数都采用单一数据类型({{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 :: 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
更宽松。
向我们提供有关您正在处理的问题的更多信息,我们可以为您提供针对您的具体情况的更多有用的建议。