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 Node
s of type Group
only may have arguments of type Arg1
and Arg2
but Item
s 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.
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.