1

Data.Tree uses a list to represent the subtree rooted at a particular node. Is it possible to have two tree types, for example one which uses a list and another which uses a vector? I want to be able to write functions which don't care how the sub-tree is represented concretely, only that the subtree is traversable, as well as functions which take advantage of a particular subtree type, e.g. fast indexing into vectors.

It seems like type families would be the right tool for the job, though I've never used them before and I have no idea how to actually define the right family.

If it matters, I'm not using the containers library tree instance, but instead I have types

data Tree a b = Node a b [Tree a b] deriving (Show, Foldable, Generic)

and

data MassivTree a b = V a b (Array B Ix1 (MassivTree a b))

where the latter uses vectors from massiv.

loke202
  • 13
  • 2
  • 3
    See the [`Cofree`](https://hackage.haskell.org/package/free-5.1.6/docs/Control-Comonad-Cofree.html) datatype; `Cofree f a = a :< f (Cofree f a)` – Iceland_jack Apr 22 '21 at 14:41
  • 2
    And its dual; [`Free f a = Pure a | Free (f (Free f a))`](https://hackage.haskell.org/package/free-5.1.6/docs/Control-Monad-Free.html) – Iceland_jack Apr 22 '21 at 14:42

1 Answers1

4

You could use a typeclass - in fact the typeclass you need probably already exists.

Consider this:

data Tree t a = Tree a (t (Tree t a))

Argument t is a higher-kinded type which represents a container of as.

Now define a set of Tree operations, constrained on Traversable like so:

:: (Foldable t) => Tree t a -> b

And you can now create and manipulate trees that use any Foldable. You would need to choose the right typeclass for the set of operations you want - Functor may be enough, or you may want Traversable if you are doing anything with monadic actions. You can choose the typeclass on a per-function basis, depending on what it does.

You can now define Tree types like so:

type ListTree a = Tree [] a
type MassivTree r ix a = Tree (Array r ix) a

You can also define instance-specific functions, with access to a full range of functionality:

:: ListTree a -> b
-- or
:: Tree [] a -> b

Happy Haskelling!

Ari Fordsham
  • 2,437
  • 7
  • 28
  • Thanks. Was able to get it make my current tree type a specialization of Tree t a b with minimal re-writing (just had to make it an instance of Show manually). And all the old functions still work. – loke202 Apr 22 '21 at 12:39
  • This is just `Cofree` – xgrommx Apr 22 '21 at 15:24
  • 1
    @xgrommx Can you expand on that? – Paul Johnson Apr 22 '21 at 21:15
  • Tree is Cofree here – xgrommx Apr 22 '21 at 23:07
  • I understand. can you explain the significance? I assume there is some category-theoretical way of looking at that. There's no need to give things new, obscure names without some sort of benefit. What is a "comonad homomorphism from another comonad that is is equivalent to a natural transformation", and is there a reason for me to care in this context? – Ari Fordsham Apr 23 '21 at 12:27
  • 1
    @AriFordsham The reason to care is that there's already a library that provides this data type and a bunch of operations on it, including folding, unfolding, traversing, zipping, extracting values, and more. That's a bunch of code you don't have to write yourself. – Daniel Wagner Apr 23 '21 at 15:37
  • Granted (although it seems in this case the OP *wanted* to write this code himself). It's just a shame we have to bonk new Haskellers over the head with category-theoretic jargon, and when asked, we cannot provide a concrete justification about what the category theory gives us. – Ari Fordsham Apr 25 '21 at 11:29