2

I've come across these discussions of mutual recursion, initially in the context of the let expression, i.e., a let allows for bindings to refer to one another with no mind for order. (See here.) I then saw this discussion in Wikipedia. This gives an example in the form of a data type in SML

datatype 'a tree = Empty | Node of 'a * 'a forest
and      'a forest = Nil | Cons of 'a tree * 'a forest

As I recall, the and is necessary for a mutual recursive relationship. It led to this version in Haskell

data Tree a = Empty
            | Node (a, Forest a)
data Forest a = Nil
              | Cons (Tree a) (Forest a)

I have an intuitive grasp of this, but the syntax is confusion, e.g., the SML tree has

... Node of 'a * 'a forest

This is a tuple, correct? And yes, I think I'm seeing a tuple in the Haskell translation

... Node (a, Forest a)

Intuitively, this is the same as a branch capability where a tree Node may have a whole Forest of (one or many) sub-trees, correct? But then the Haskell Forest data type basically conses a new Tree element to the head of a Forest (of Tree elements) list, and yet the SML version seems to use a *

... Cons of 'a tree * 'a forest

which means create a tuple? In general, the confusing Tree definition, i.e., a Tree can have a Forest underneath it. Then there is this definition of a tree

data Tree = Leaf | Node Int Tree Tree

where the issue of sub-trees is limited to two sub-trees. Is using Forest the only way to allow one-or-many sub-nodes? Intuitively, trees don't have forests under them, rather, they are members of a forest. One question would be, Can there be a direct use of regular list consing?

data Forest a = Nil | ForestCons (Tree a) [(Tree a)] deriving (Show)
147pm
  • 2,137
  • 18
  • 28
  • 5
    The tuple is redundant; `data Tree a = Empty | Node a (Forest a)` is fine. – chepner Jan 31 '21 at 22:42
  • 1
    In the SML syntax `Node of 'a * 'a Tree` is not a tuple despite looking like one. – Fyodor Soikin Feb 01 '21 at 00:55
  • 3
    As far as a direct use of regular lists, I think what you're looking for is `type Forest a = [Tree a]`. You can then combine the types and write `data Tree a = Empty | Node a [Tree a]` – DDub Feb 01 '21 at 02:17
  • You may find the `GADTSyntax` language option helpful; instead of this shorthand syntax for ADTs, it lets you write a `data` definition as a list of the type signatures of the constructors that you would see if you asked e.g. `:type Node` in GHCi. In this case, I’d write `data Tree a where { Empty :: Tree a; Node :: a -> Forest a -> Tree a; }` and `data Forest a where { Nil :: Forest a; Cons :: Tree a -> Forest a -> Forest a; }` or the uncurried forms `Node :: (a, Forest a) -> Tree a` & `Cons :: (Tree a, Forest a) -> Forest a` if you include the (unnecessary) tuples. – Jon Purdy Feb 02 '21 at 23:02

1 Answers1

6

Node (a, Forest a), despite being perfectly legal Haskell, is unidiomatic. The conventional form of the definition would be

data Tree' a = Empty'
             | Node' a (Forest a)

This contains the same information as your Tree, but instead of packing the fields of the Node constructor in a tuple it just gives them both as fields. The difference is that, when actually dealing with values of the type, you'd write Node' h t instead of Node' (h,t).

Likewise, your Forest is equivalent to the not recommended form

data Forest' a = Nil'
               | Cons' (Tree a, Forest a)
leftaroundabout
  • 117,950
  • 5
  • 174
  • 319
  • What would be an idiomatic form for `Forest` if not the given one? – 147pm Feb 01 '21 at 04:31
  • 1
    `newtype Forest = Forest { treesList :: [Tree] }`. But your form with `Cons (Tree a) (Forest a)` is also idiomatic, just making things unnecessarily complicated. – leftaroundabout Feb 01 '21 at 07:32
  • 1
    Indeed, even a simple `type` alias, rather than a `newtype`, [would be idiomatic](https://hackage.haskell.org/package/containers-0.6.4.1/docs/Data-Graph.html#t:Forest). – Daniel Wagner Feb 02 '21 at 00:19