Search code examples
haskellfunctional-programmingxml-serializationtypeclassabstract-data-type

Haskell rendering adt as xml design problem


I am a beginner at haskell, and I hit a wall during a toy project. I am simply trying to model a unit and a container which can units and containers as members. I want to serialize the unit and the container as xml maybe other formats afterwards. Here is what I have so far

module Model where
import           Data.Map                       ( Map )  -- importing type
import           Data.Text                      ( Text )

data ModelInfo = InfoCons {
                    modelId :: Text, modelType :: Text,
                    modelAttrs :: Map Text Text
                } deriving (Show, Eq)

data UnitModel = UnitCons { unitInfo :: ModelInfo, unitData :: Text} deriving (Show, Eq)

data ContainerModel = ContainerCons {  containerInfo :: ModelInfo
                       , containerData :: [ContainerData]} deriving (Show, Eq)


data ContainerData = NestedCons ContainerModel
                    | SimpleCons UnitModel
                     deriving (Show, Eq)

These are my models. They are mostly inspired from here. Here are my rendering functions:


class ModelRenderer model where
    -- function definition
    addIdType2Props :: (model -> ModelInfo) -> model -> Map Txt.Text Txt.Text
    addIdType2Props f mdl = add2Map
        (modelAttrs (f mdl))
        [(Txt.pack "id", modelId (f mdl)),
         (Txt.pack "type", modelType (f mdl))]

    makeElement :: model -> Xml.Element


-- transform unit model to xml

instance ModelRenderer UnitModel where
    makeElement um = Xml.Element
        { Xml.elementName       = makeName (Txt.pack "unit")
        , Xml.elementAttributes = convertTxt2NameMap
                                      (addIdType2Props unitInfo um)
        , Xml.elementNodes      = [Xml.NodeContent (unitData um)]
        }


-- transform container model to xml
instance ModelRenderer ContainerModel where
    makeElement cm = Xml.Element
        { Xml.elementName       = makeName (Txt.pack "container")
        , Xml.elementAttributes = convertTxt2NameMap
                                      (addIdType2Props containerInfo cm)
        , Xml.elementNodes      = map (Xml.NodeElement . makeElement)
                                      (containerData cm)
        }

The problem is I can't figure out how to render ContainerData with my current knowledge on haskell. I want to typecheck the value that constructs the ContainerData but I know that it is not the haskell way. Any suggestions ?


Solution

  • I think you can use pattern matching for that. Something like

    instance ModelRenderer ContainerData where
        makeElement (NestedCons cm) = makeElement cm
        makeElement (SimpleCons um) = makeElement um
    

    You can adapt this to the full ContainerModel, adding the tags you wish.