5

I'm new to F# and learning the basics.

I have two modules. A generic one for tree data structures called Tree:

module Tree

let rec getDescendants getChildren node  = 
    seq { yield node
          for child in getChildren node do 
            yield! getDescendants getChildren child }

let isLeaf getChildren node = Seq.isEmpty (getChildren node)

let getLeaves getChildren node = getDescendants getChildren node  
                               |> Seq.filter (isLeaf getChildren)

As you can see, all functions have a getChildren argument, which is a function that enumerates the children of a node of a given type.

The second module handles the more specific case of XML trees:

module XmlTree

open System.Xml.Linq

let getXmlChildren (node : XElement) = node.Elements()

let getDescendants = Tree.getDescendants getXmlChildren 
let getLeaves = Tree.getLeaves getXmlChildren 
let isLeaf = Tree.isLeaf getXmlChildren 

A specific getXmlChildren function for XML nodes is defined and passed to the curried Tree functions.

Now there is an awful lot of code duplication.

Is it somehow possible to do the following? (pseudocode)

module XmlTree = Tree with getChildren = fun (node : XElement) -> node.Elements()
3dGrabber
  • 4,710
  • 1
  • 34
  • 42

2 Answers2

12

F# doesn't support functors so you cannot pass parameters to F# modules. In your example, passing a function which generates children of a node to object constructors is enough:

type Tree<'T>(childFn: 'T -> 'T seq) =
    let getChildren = childFn

    member x.getDescendants node  = 
        seq { yield node
              for child in getChildren node do 
                yield! x.getDescendants child }

    member x.isLeaf node = node |> getChildren |> Seq.isEmpty
    member x.getLeaves node = node |> x.getDescendants |> Seq.filter x.isLeaf

// Type usage
open System.Xml.Linq
let xmlTree = new Tree<XElement>(fun x -> x.Elements())

For more sophisticated cases, inheritance is the way to go. Particularly, you can declare Tree<'T> as an abstract class with abstract member getChildren, and override that method in XmlTree subclass.

Community
  • 1
  • 1
pad
  • 41,040
  • 7
  • 92
  • 166
6

You don't do this with modules, but instead with generics, for example

EDIT:

type tree<'t>(Children:seq<tree<'t>>)=

    member x.isLeaf()  = Seq.isEmpty (Children )

    member x.getLeaves() = 
        getDescendants Children
        |> Seq.filter (fun x -> x.isLeaf())

I left out getdescendants, but that should be enough. Also, some of the type annotations aren't required, but are showing what is happening

John Palmer
  • 25,356
  • 3
  • 48
  • 67
  • Can you be a bit more specific please? How would the above example look like using generics? thx – 3dGrabber May 14 '12 at 10:43
  • John, it seems that you've provided a generic **type** that can **represent** a tree of any node type, but the question was asking about a generic **module** that can **operate** on a tree of any node type. – TheQuickBrownFox Nov 14 '15 at 16:25