% Extensible trees % [Public domain] \input birdstyle \birdleftrule=1pt \emergencystretch=1em \def\hugebreak{\penalty-600\vskip 30pt plus 8pt minus 4pt\relax} \newcount\chapno \def\: #1.{\advance\chapno by 1\relax\hugebreak{\bf\S\the\chapno. #1. }} \: Introduction. This module implements extensible compile-time trees. Each node has a key, and the key of each node is of a different type. All nodes have the same type for the value of the nodes, however. In addition, the values of the nodes can depend on the value of the key. > {-# LANGUAGE MultiParamTypeClasses, GADTs, TemplateHaskell #-} > {-# LANGUAGE FunctionalDependencies #-} > module Data.Extensible.Tree ( > ExtTreeData(..), ExtTree(..), traceExtTree, normalParent, makeExtRoot, > ExtTreeNode(..), extAncestor, extAncestorAny > ) where { > import Control.Applicative; > import Control.Monad; > import Data.Typeable; > import Language.Haskell.TH; \: Utility Function. > bool :: x -> x -> Bool -> x; > bool x _ False = x; > bool _ x True = x; \: Implementation. The first thing is a datatype used for the class which is defined below. Due to the GADT, it requires that the parent of a node also has a parent; if it is the root node you set it to its own parent. > data ExtTreeData v p c where { > ExtRoot :: ExtTree v p p => ExtTreeData v p p; > ExtNode :: ExtTree v pp p => (c -> (v, p)) -> ExtTreeData v p c; > }; In this class, {\tt v} is the type of values in the tree, {\tt p} is the parent of this node, and {\tt c} (for ``child'') is the current node. The first method is used for traversing the tree from child to parent, and the second method is used for traversing the tree from parent to child. > class Typeable c => ExtTree v p c | c -> p, p -> v where { > treeData :: ExtTreeData v p c; > normalChild :: p -> c; > }; For the root node, the instance should always be: > {- > treeData = ExtRoot; > normalChild = id; > -} The next section contains a Template Haskell code to automatically create the instance for the root node. There is then a datatype which holds any key for a single node of a tree. > data ExtTreeNode v where { > ExtTreeNode :: ExtTree v p c => c -> ExtTreeNode v; > }; But, notice that there can be multiple roots, and a root does not even have to be exposed from a module which defines it, as long as there is some node which eventually leads to it. \: Functions. {\tt traceExtTree}: Make a list of values from a single node to the root. > traceExtTree :: ExtTree v p c => c -> [v]; > traceExtTree c = case treeData of { > ExtRoot -> []; > ExtNode f -> (\(v, p) -> v : traceExtTree p) $ f c; > }; {\tt normalParent}: Given a value of a key for one node of the tree, get the corresponding value of the type of key for the parent node. > normalParent :: ExtTree v p c => c -> p; > normalParent c = case treeData of { > ExtRoot -> c; > ExtNode f -> snd (f c); > }; {\tt extAncestor}: Find the key of an ancestor of a specified node. > extAncestor :: (Typeable p, ExtTree v pp c) => c -> Maybe p; > extAncestor c = cast c <|> case treeData of { > ExtRoot -> Nothing; > ExtNode f -> extAncestor $ snd (f c); > }; {\tt extAncestorAny}: Like the above function, but in a container storing a key of any node. > extAncestorAny :: Typeable p => ExtTreeNode v -> Maybe p; > extAncestorAny (ExtTreeNode c) = extAncestor c; Here is a Template Haskell macro to create the instance for the root node. Due to some fault with parsing of Template Haskell quotations, this won't compile unless both {\tt FlexibleInstances} and {\tt UndecidableInstances} extensions are enabled in this module. (You do not need to enable those extensions in the module using this function.) > makeExtRoot :: Q Type -> Q Type -> Q [Dec]; > makeExtRoot = liftM2 $ \v p -> [InstanceD [] ( > AppT (AppT (AppT (ConT ''ExtTree) v) p) p > ) [ > ValD (VarP 'treeData) (NormalB $ ConE 'ExtRoot) [], > ValD (VarP 'normalChild) (NormalB $ VarE 'id) [] > ]]; % End of document (final "}" is suppressed from printout) \medskip\centerline{The End} \toks0={{ > } -- }\bye