user3644664
user3644664

Reputation: 17

Haskell to parse XML document

I am actually new to haskell. Trying to run this code:

Return the tag name of a XML element

getName :: Content -> Name
getName (CElem (Elem nme atts cs)) = nme 

I've got the following error:

Expecting one more argument to `Content'
    In the type signature for `getName': getName :: Content -> Name

could you please help me to fix it.

Upvotes: 0

Views: 152

Answers (1)

crockeea
crockeea

Reputation: 21811

There are several problem with the code you posted, all of which can be seen by looking at the types alone. Here is the relevant type information from the docs:

data Content i = CElem (Element i) i
               | CString Bool CharData i
               | CRef Reference i
               | CMisc Misc i
               deriving Show

data Element i = Elem QName [Attribute] [Content i] deriving (Eq, Show)

data QName = N  Name
           | QN Namespace Name 
           deriving (Eq,Show)

data Reference = RefEntity EntityRef
               | RefChar CharRef
               deriving (Eq,Show)
type EntityRef    = Name

Here are your problems with the posted code:

  1. The type Content has kind * -> *, that is, Content is a type constructor which takes a type parameter as an argument and returns a type. You can't pass a partially applied type (Content) as a value to a function, which means your getName function should have the signature

    getName :: Content i -> Name
    
  2. Your pattern match on CElem only has one argument, but you can see from its definition above that it takes two. Since you are only using one of the constructor values, we can replace the others with _. So far, we have this:

    getName :: Content i -> Name
    getName (CElem (Elem nme _ _) _) = nme
    
  3. The first argument of the Elem constructor has type QName, but your signature suggests you want to return something of type Name. This means we can't just return nme. Instead, we need to pattern match again on the N constructor to pull out the name:

    getName :: Content i -> Name
    getName (CElem (Elem (N nme) _ _) _) = nme
    

While the final definition should compile, you'll likely get runtime errors due to a refutable pattern match. This means you will try to match a value against a pattern, but the pattern is not found. For example, the first argument of Elem has type QName, but could instead be constructed using the QN constructor. Thus it would be better if we add a case for that constructor:

getName (CElem (Elem (QN _ nme) _ _) _) = nme

Similarly, you should consider all of the other possibilities for a refutable pattern match: the Elem constructor is fine; its type Element i only has one constructor. The type Content i has multiple constructors. The CString constructor doesn't have an obviously-derivable value of type Name, so we'll ignore it for now. CRef does, so we'll add a case for it:

 getName (CRef (RefEntity nme) _) = nme

The other constructor for Reference doesn't have a name, so we ignore it. I'll leave CMisc up to you.

 This means that the final definition of `getName` should looks something like this:

 getName :: Content i -> Name
 getName (CElem (Elem (N nme) _ _) _) = nme
 getName (CElem (Elem (QN _ nme) _ _) _) = nme
 getName (CRef (RefEntity nme) _) = nme
 -- maybe add a case for CMisc here
 getName _ = error "Content constructor doesn't have a Name!"

Upvotes: 2

Related Questions