Не могли бы вы объяснить мне функторы OCaml?

Возможный дубликат:
Что в функциональном программировании такое функтор?

Я мало что знаю об OCaml, я некоторое время изучал F # и хорошо его понимаю.

Говорят, что в F # отсутствует модель функторов, которая присутствует в OCaml. Я пытался понять, что такое функтор, но википедия и учебные пособия мне не очень помогли.

Не могли бы вы осветить мне эту тайну? Заранее спасибо :)

РЕДАКТИРОВАТЬ:

Я уловил суть, спасибо всем, кто мне помог. Вы можете закрыть вопрос как точную копию: В функциональном программировании, что такое функтор?


person Bubba88    schedule 03.03.2010    source источник
comment
Я не гуру ни F #, ни OCaml, поэтому я не буду делать это как формальный ответ, но думаю, что blog.matthewdoig.com/?p=152 может немного помочь вам в объяснении функторов и того, как F # справляется с их отсутствием.   -  person JUST MY correct OPINION    schedule 03.03.2010
comment
Я сначала прочитал эту статью, есть еще blog.matthewdoig.com/?p=155. Я на полпути к пониманию этой вещи :)   -  person Bubba88    schedule 03.03.2010


Ответы (3)


Если вы пришли из вселенной ООП, то, вероятно, полезно думать о модуле как о аналоге статического класса. Подобно статическим классам .NET, модуль OCaml имеет конструкторы; В отличие от .NET, модули OCaml могут принимать параметры в своих конструкторах. Функтор - это пугающее название для объекта, который вы передаете в конструктор модуля.

Итак, используя канонический пример двоичного дерева, мы обычно пишем его на F # следующим образом:

type 'a tree =
    | Nil
    | Node of 'a tree * 'a * 'a tree

module Tree =
    let rec insert v = function
        | Nil -> Node(Nil, v, Nil)
        | Node(l, x, r) ->
            if v < x then Node(insert v l, x, r)
            elif v > x then Node(l, x, insert v r)
            else Node(l, x, r)

Прекрасно и стильно. Но как F # знает, как сравнивать два объекта типа 'a с помощью операторов < и >?

За кулисами он делает что-то вроде этого:

> let gt x y = x > y;;

val gt : 'a -> 'a -> bool when 'a : comparison

Хорошо, а что, если у вас есть объект типа Person, который не реализует этот конкретный интерфейс? Что, если вы хотите определить функцию сортировки на лету? Один из подходов - просто передать компаратор следующим образом:

    let rec insert comparer v = function
        | Nil -> Node(Nil, v, Nil)
        | Node(l, x, r) ->
            if comparer v x = 1 then Node(insert v l, x, r)
            elif comparer v x = -1 then Node(l, x, insert v r)
            else Node(l, x, r)

Это работает, но если вы пишете модуль для операций с деревом со вставкой, поиском, удалением и т. Д., Вам нужно, чтобы клиенты передавали функцию упорядочивания каждый раз, когда они что-либо вызывают.

Если F # поддерживает функторы, его гипотетический синтаксис может выглядеть следующим образом:

type 'a Comparer =
    abstract Gt : 'a -> 'a -> bool
    abstract Lt : 'a -> 'a -> bool
    abstract Eq : 'a -> 'a -> bool

module Tree (comparer : 'a Comparer) =
    let rec insert v = function
        | Nil -> Node(Nil, v, Nil)
        | Node(l, x, r) ->
            if comparer.Lt v x then Node(insert v l, x, r)
            elif comparer.Gt v x then Node(l, x, insert v r)
            else Node(l, x, r)

По-прежнему в гипотетическом синтаксисе вы должны создать свой модуль как таковой:

module PersonTree = Tree (new Comparer<Person>
    {
        member this.Lt x y = x.LastName < y.LastName
        member this.Gt x y = x.LastName > y.LastName
        member this.Eq x y = x.LastName = y.LastName
    })

let people = PersonTree.insert 1 Nil

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

type 'a Tree =
    | Nil of 'a -> 'a -> int
    | Node of 'a -> 'a -> int * 'a tree * 'a * 'a tree

module Tree =
    let comparer = function
        | Nil(f) -> f
        | Node(f, _, _, _) -> f

    let empty f = Nil(f)

    let make (l, x, r) =
        let f = comparer l
        Node(f, l, x, r)

    let rec insert v = function
        | Nil(_) -> make(Nil, v, Nil)
        | Node(f, l, x, r) ->
            if f v x = -1 then make(insert v l, x, r)
            elif f v x = 1 then make(l, x, insert v r)
            else make(l, x, r)

let people = Tree.empty (function x y -> x.LastName.CompareTo(y.LastName))
person Juliet    schedule 03.03.2010
comment
Спасибо за этот полезный пример. Вам удалось это сделать без строчки кода Ocaml, ага)) - person Bubba88; 03.03.2010
comment
Чем это отличается от принятия экземпляра IEqualityComparer, например, в конструкторе? Это может быть реализовано специальным образом с использованием объектных выражений. - person Daniel; 03.03.2010
comment
@ Дэниел: в этом суть. Функтор OCaml - это по сути то же самое, что передать какой-то объект в конструктор модуля. Но поскольку модули F # == статические классы .NET, а статические конструкторы .NET не принимают параметры в своих конструкторах, вам нужно перепрыгнуть через обручи, чтобы получить такую ​​же функциональность. - person Juliet; 03.03.2010

Функторы - это модули, параметризованные модулями, то есть отражение от модулей к модулям (обычная функция - это отражение от значений к значениям, полиморфная функция - это отражение от типов к обычным функциям).

См. Также ocaml-tutorial по модулям.

Примеры в руководстве также полезны.

person ygrek    schedule 03.03.2010
comment
Я опоздал на 10 секунд :-) Я не специалист, и я понял это объяснение. - person yogsototh; 03.03.2010
comment
Это хорошее объяснение того, что такое функторы OCaml и что они хороши, так это программирование общих структур данных (см. Реализацию Set в стандартной библиотеке OCaml, возможно, это простейший пример функтора) и, как правило, предлагает эффективное альтернативное решение с статической проверкой типов для многих проблем, которые вы также можете решить с помощью объектно-ориентированного программирования. - person Pascal Cuoq; 03.03.2010
comment
Жаль, что мне не повезло) .. Итак, если я правильно понимаю: функторы - это способ создания новых модулей путем параметризации существующих модулей, таких как Set? - person Bubba88; 03.03.2010
comment
Точно так же, как дженерики предназначены для классов, функторы предназначены для модулей. Это правильный способ мыслить? - person Bubba88; 03.03.2010
comment
Чтобы представить набор в виде двоичного дерева, вам понадобится функция упорядочивания. Тип элементов и их функция упорядочивания составляют модуль Elt. Функтор Set создает тип и функции доступа для наборов из модуля Elt. - person Pascal Cuoq; 03.03.2010
comment
Насколько я понимаю, дженерики в объектно-ориентированных языках более сопоставимы с параметрическим полиморфизмом, который у вас уже есть в F # и OCaml (пример: 'список). Параметрического полиморфизма недостаточно, когда недостаточно абстрактного типа (пример: для построения наборов в виде двоичных деревьев вам нужна функция упорядочивания по элементам). - person Pascal Cuoq; 03.03.2010

Ознакомьтесь с этими структурами данных в курсе ocaml:

http://www.cs.cornell.edu/Courses/cs3110/2009fa/lecturenotes.asp

лекция по функтору: http://www.cs.cornell.edu/Courses/cs3110/2009fa/lectures/lec10.html

и реализация расширяемого дерева с использованием функтора: http://www.cs.cornell.edu/Courses/cs3110/2009fa/recitations/rec-splay.html

person Yin Zhu    schedule 03.03.2010