如何限制异构列表中的类型?

时间:2013-01-15 13:17:31

标签: haskell dsl gadt

我目前正在尝试创建嵌入Haskell的(类型)类型安全的xml语法。最后,我希望实现这样的目标:

tree = group [arg1 "str", arg2 42] 
         [item [foo, bar] []
         ,item [foo, bar] []
         ]

其中group和item属于Node :: [Arg t] -> [Node c] -> Node t种。如果这没有任何意义,那很可能是因为我不知道我在做什么:)

我现在的问题是如何使类型系统阻止我向Node提供'错误'参数。例如,Node类型的Group只能包含Arg1Arg2类型的参数,但Item可能包含Foo类型的参数和{ {1}}。

我想底线问题是:如何限制异类列表中的类型?


我想要实现的(用户)语法示例:

Bar

其中(。:)是一个在节点中设置参数的函数。这将代表一个包含两个项目的参数的组。

此外还会有一些(伪)定义,如:

group .: arg1 "str" .: arg2 42
    item .: foo .: bar
    item .: foo .: bar

我正在寻找一种方法来捕捉类型检测器的使用错误。

2 个答案:

答案 0 :(得分:3)

你所拥有的东西并不像我需要一个异类列表。也许你正在寻找这样的东西?

data Foo = Foo Int

data Bar = Bar Int

data Arg = StringArg String | IntArg Int | DoubleArg Double

data Tree = Group Arg Arg [Item]

data Item = Item Foo Bar

example :: Tree
example = Group (StringArg "str") (IntArg 42)
            [Item (Foo 1) (Bar 2), Item (Foo 12) (Bar 36)]

请注意,我们甚至可以创建Arg个不同“子类型”的列表。例如,[StringArg "hello", IntArg 3, DoubleArg 12.0]。不过,它仍然是一个同类的清单。

=====编辑=====

有几种方法可以处理“默认参数”的情况。假设项中的Bar参数是可选的。我的第一个想法是,虽然用户可以选择指定它,但是当我存储数据时我想要包含默认参数。那样, 确定默认值与实际对其执行某些操作的代码分开。所以, 如果用户指定Foo为3,但未提供Bar,且默认为Bar 77,则我将项目创建为:

Item (Foo 3) (Bar 77)

这样做的好处是,对这个对象进行操作的函数不需要担心默认值;就这些参数而言,这两个参数总是存在。

但是,如果你真的想省略数据结构中的默认参数,你可以做这样的事情:

data Bar = Bar Int | DefaultBar

example = Group (StringArg "str") (IntArg 42)
            [Item (Foo 1) (Bar 2), Item (Foo 12) DefaultBar]

甚至:

data Item = Item Foo Bar | ItemWithDefaultBar Foo

=====编辑#2 =====

所以也许你可以使用这样的东西:

data ComplicatedItem = ComplicatedItem 
    {
      location :: (Double, Double),
      size :: Int,
      rotation :: Double,
      . . . and so on . . .
    }

defaultComplicatedItem = ComplicatedItem { location = (0.0,0.0), size = 1, rotation = 0.0), ... }

要创建ComplicatedItem,用户只需指定非默认参数:

myComplicatedItem = defaultComplicatedItem { size=3 }

如果您向ComplicatedItem类型添加新参数,则需要更新defaultComplicatedItem,但myComplicatedItem的定义不会更改。

您还可以覆盖show功能,以便在打印时省略默认参数。

答案 1 :(得分:1)

基于随后的讨论,听起来你想要的是创建一个DSL(特定于域的语言)来表示XML。

一个选项是在Haskell中嵌入你的DSL,因此它可以出现在Haskell源代码中。通常,您可以通过定义所需的类型并提供一组函数来处理这些类型来完成此操作。 听起来这就是你希望做的事情。然而,作为嵌入式 DSL,它会受到一些限制,这就是你遇到的问题遇到。也许有一个聪明的伎俩可以做你想要的,也许是涉及类型功能的东西,但我现在想不出任何东西。如果您想继续尝试,可以在您的问题中添加标记dslgadt,吸引那些比我更了解这些内容的人的注意力。或者,您可以使用Template HaskellScrap Your Boilerplate之类的内容来允许用户省略某些信息,这些信息会在Haskell“看到”之前被“填入”。

另一种选择是使用外部DSL,您可以使用Haskell进行解析。您可以定义DSL,但也许直接使用XML直接使用合适的DTD会更容易。当然,还有用于解析XML的Haskell库。