Search code examples
haskelldslgadt

How do I restrict the types in a heterogenous list?


I am currently trying to create a (sort of) typesafe xml like syntax embedded into Haskell. In the end I am hoping to achieve something like this:

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

where group and item are of kind Node :: [Arg t] -> [Node c] -> Node t. If this doesn't make any sense it is most probably because I have no idea what I am doing :)

My question now is how to make the type system prevent me from giving 'wrong' arguments to a Node. Eg Nodes of type Group only may have arguments of type Arg1 and Arg2 but Items may have arguments of type Foo and Bar.

I guess the bottom line question is: how do i restrict the types in a heterogenous list?


Example of the (user) syntax i am trying to achieve:

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

where (.:) is a function that sets the parameter in the node. This would represent a group with some parameters containing two items.

Additionally there would be some (pseudo) definition like:

data Node = Node PossibleArguments PossibleChildNodes

type Group = Node [Arg1, Arg2] [Item]
type Item  = Node [Foo, Bar] []

I am searching for a way to catch usage errors by the typechecker.


Solution

  • Based on the ensuing discussion, it sounds like what you want is to create a DSL (Domain-Specific Language) to represent XML.

    One option is to embed your DSL in Haskell so it can appear in Haskell source code. In general, you can do this by defining the types you need, and providing a set of functions to work with those types. It sounds like this is what you're hoping to do. However, as an embedded DSL, it will be subject to some constraints, and this is the problem you're encountering. Perhaps there is a clever trick to do what you want, maybe something involving type functions, but I can't think of anything at present. If you want to keep trying, maybe add the tags dsl and gadt to your question, catch the attention of people who know more about this stuff than I do. Alternatively, you might be able to use something like Template Haskell or Scrap Your Boilerplate to allow your users to omit some information, which would them be "filled in" before Haskell "sees" it.

    Another option is to have an external DSL, which you parse using Haskell. You could define a DSL, but maybe it would be easier to just use XML directly with a suitable DTD. There are Haskell libraries for parsing XML, of course.