Можно ли передавать параметры в модули F #?

Я новичок в F # и изучаю основы.

У меня есть два модуля. Универсальный для древовидных структур данных называется 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)

Как видите, все функции имеют аргумент getChildren, который перечисляет дочерние элементы узла данного типа.

Второй модуль обрабатывает более конкретный случай деревьев XML:

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 

getXmlChildren функция getXmlChildren для узлов XML определяется и передается в карри функции Tree.

Сейчас очень много дублирования кода.

Можно ли как-то сделать следующее? (Псевдокод)

module XmlTree = Tree with getChildren = fun (node : XElement) -> node.Elements()

Ответы

Ответ 1

F # не поддерживает functors, поэтому вы не можете передавать параметры модулям F #. В вашем примере достаточно передать функцию, которая генерирует дочерние элементы конструктора node для объекта:

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())

Для более сложных случаев наследование - это путь. В частности, вы можете объявить Tree<'T> как абстрактный класс с абстрактным элементом getChildren и переопределить этот метод в подклассе XmlTree.

Ответ 2

Вы не делаете этого с помощью модулей, а вместо этого с помощью дженериков, например

ИЗМЕНИТЬ

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

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

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

Я ушел из getdescendants, но этого должно быть достаточно. Кроме того, некоторые аннотации типа не требуются, но показывают, что происходит