polytree: A polymorphic rose-tree

[ bsd3, data, library ] [ Propose Tags ] [ Report a vulnerability ]

A rose-tree which has different data in the nodes and leaves


[Skip to Readme]

Flags

Manual Flags

NameDescriptionDefault
dev

Enable -Werror for development

Disabled

Use -f <flag> to enable a flag, or -f -<flag> to disable that flag. More info

Downloads

Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees

Candidates

  • No Candidates
Versions [RSS] 0.0.1, 0.0.2, 0.0.3, 0.0.4, 0.0.5, 0.0.6, 0.0.7, 0.0.8, 0.0.9, 0.0.10, 0.1.0, 0.1.1, 0.1.2
Change log changelog.md
Dependencies base (>=4.9 && <6), bifunctors (>=5.6 && <7), containers (>=0.6.7 && <1), free (>=5.2 && <6), lens (>=5 && <6), semigroupoids (>=6.0.1 && <7) [details]
Tested with ghc ==9.4.8, ghc ==9.6.5, ghc ==9.8.4, ghc ==9.10.3
License BSD-3-Clause
Copyright Copyright (C) 2025-2026 Tony Morris
Author Tony Morris <ʇǝu˙sıɹɹoɯʇ@ןןǝʞsɐɥ>
Maintainer Tony Morris <ʇǝu˙sıɹɹoɯʇ@ןןǝʞsɐɥ>
Uploaded by TonyMorris at 2026-05-18T01:28:10Z
Category Data
Home page https://gitlab.com/tonymorris/polytree
Bug tracker https://gitlab.com/tonymorris/polytree/issues
Source repo head: git clone git@gitlab.com:tonymorris/polytree.git
Distributions
Downloads 172 total (33 in the last 30 days)
Rating (no votes yet) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Docs available [build log]
Last success reported on 2026-05-18 [all 1 reports]

Readme for polytree-0.1.2

[back to package description]

polytree

A polymorphic rose tree with different types for node labels and leaf values.

Overview

The polytree library provides Tree f a b and TreeForest f a b data types where:

  • f is a polymorphic container type (list, vector, etc.)
  • a is the type of node labels
  • b is the type of leaf values

This design allows for flexible tree representations where internal nodes and leaves can have different types, and the choice of container affects performance characteristics.

Key Features

Data Types

  • Tree f a b - A tree with labels of type a at nodes and values of type b at leaves
  • TreeForest f a b - A forest (collection) of trees and leaves
  • Type aliases: Tree', TreeList, TreeList', Tree1, Tree1'

Type Class Instances

Comprehensive instances for:

  • Standard classes: Eq, Ord, Show (with lifted variants Eq1, Eq2, etc.)
  • Functors: Bifunctor, Functor
  • Applicatives: Apply, Applicative (operates over leaves with Monoid/Semigroup on labels)
  • Foldables: Bifoldable, Foldable, Bifoldable1, Foldable1
  • Traversables: Bitraversable, Traversable, Bitraversable1, Traversable1
  • Lens integration: Wrapped, Plated, and custom optics

Optics

Four-level classy optics hierarchy:

  • GetX - Read-only access via Getter
  • HasX - Read-write access via Lens'
  • ReviewX - Construction via Review
  • AsX - Full prism access via Prism'

Available for both Tree and TreeForest types.

Utility Functions

  • Construction: makeTree, makeChild, makeLeaves, makeChildren, singleton
  • Unfolding: unfoldTree, unfoldTreeM
  • Traversal: dfs (depth-first), bfs (breadth-first)
  • Analysis: countNodes, countLeaves, levels
  • Transformation: pruneLeaves
  • Conversion: baseTree (to/from Data.Tree.Tree)

Example Usage

import Data.PolyTree
import Control.Lens

-- Create a tree with string labels and integer leaves
tree :: TreeList String Int
tree = Tree "root" 
         (TreeForest 
           [ Left 42                    -- A leaf
           , makeChild "child1" [Left 10, Left 20]  -- A subtree
           , makeChild "child2" []      -- An empty subtree
           ])

-- Traverse leaves
>>> toListOf treeLeaves tree
[42,10,20]

-- Map over leaves
>>> fmap (*2) tree
Tree "root" (TreeForest [Left 84,Right (Tree "child1" (TreeForest [Left 20,Left 40])),Right (Tree "child2" (TreeForest []))])

-- Depth-first traversal
>>> dfs tree
Left "root" :| [Right 42,Left "child1",Right 10,Right 20,Left "child2"]

-- Breadth-first traversal
>>> bfs tree
Left "root" :| [Right 42,Left "child1",Left "child2",Right 10,Right 20]

-- Unfold a tree
>>> unfoldTree (\n -> (n, if n > 0 then [Right (n-1)] else [])) 3
Tree 3 (TreeForest [Right (Tree 2 (TreeForest [Right (Tree 1 (TreeForest [Right (Tree 0 (TreeForest []))]))]))])

-- Use applicative instance (operates over leaves with Monoid on labels)
>>> Tree "a" (TreeForest [Left (+1)]) <*> Tree "b" (TreeForest [Left 5])
Tree "ab" (TreeForest [Left 6])

Design Decisions

Why Different Types for Nodes and Leaves?

Many tree algorithms distinguish between internal nodes (which have structural/organizational data) and leaves (which have payload data). For example:

  • Decision trees: nodes contain split criteria, leaves contain predictions
  • Expression trees: nodes contain operators, leaves contain values
  • File systems: nodes are directories, leaves are files

Why Polymorphic Container?

The f parameter allows you to choose the container type:

  • [] for simple lists (default, most flexible)
  • Vector for efficient random access
  • NonEmpty for non-empty forests (with Foldable1/Traversable1 instances)
  • Identity for single-child trees

Why Not Biapplicative?

While Bifunctor, Bifoldable, and Bitraversable instances are possible, Biapply and Biapplicative instances are not implementable due to the recursive Either-based structure. The combination of a tree of functions with a single value has no canonical semantics.

Testing

The library includes comprehensive doctests (115 examples). Run with:

cabal test
  • Data.Tree from containers - Standard rose tree (single type for all nodes)
  • Data.Tree.Class - Type class approach to trees
  • This library's approach: Separate types for nodes/leaves with polymorphic container

System-F