Какая разница между функторами и "дженериками",

Я смотрю OCaml функторы. Он выглядит очень похожим на так называемые общие объекты в C++/C#/Java. Если вы проигнорируете стирание Java-типа на данный момент и проигнорируете детали реализации для шаблонов С++ (меня интересует функция языка), функторы довольно специфичны для дженериков. Если я правильно ее понимаю, функтор дает вам новый набор функций из типа, который вы предоставляете, чтобы, например,

List<MyClass>.GetType() != List<MyOtherClass>.GetType()

Но вы можете грубо переписать OCaml

#module Set =
   functor (Elt: ORDERED_TYPE) ->
     struct
       type element = Elt.t
       type set = element list
       let empty = []
       let rec add x s =
         match s with
           [] -> [x]
         | hd::tl ->
            match Elt.compare x hd with
              Equal   -> s         (* x is already in s *)
            | Less    -> x :: s    (* x is smaller than all elements of s *)
            | Greater -> hd :: add x tl
       let rec member x s =
         match s with
           [] -> false
         | hd::tl ->
             match Elt.compare x hd with
               Equal   -> true     (* x belongs to s *)
             | Less    -> false    (* x is smaller than all elements of s *)
             | Greater -> member x tl
     end;;

в C#

class Set<T> where T : ISortable
{
    private List<T> l = new List<T>();
    static public Set<T> empty = new Set<T>();
    public bool IsMember(T x) {return l.IndexOf(x) > -1;}
    public void Add(T x) {l.Add(x);}
}

Конечно, существует немного другое, поскольку функтор влияет на Module (который представляет собой просто набор функций и значений типов, аналогичных пространству имен C#).

Но так ли это? Являются ли функторы просто дженериками, применяемыми к пространствам имен? Или есть какие-то существенные различия между функторами и дженериками, которые мне не хватает.

Даже если функторы - это просто дженерики для пространства имен, каково существенное преимущество этого подхода? Классы также могут использоваться как специальные пространства имен с использованием вложенных классов.

Ответы

Ответ 1

Но так ли это? Просто ли функторы генерики, применяемые к пространствам имен?

Да, я думаю, что функторы могут рассматривать функторы как "пространства имен с generics", и это само по себе будет very приветствовать в С++, где единственная опция - использовать классы со всеми статическими членами, которые становятся довольно уродливо скоро. По сравнению с шаблонами С++ одним из огромных преимуществ является явная подпись на параметрах модуля (это то, что я считаю концепциями С++ 0x, но oops),

Также модули сильно отличаются от пространств имен (рассмотрим несколько структурных подписи, абстрактные и частные типы).

Даже если функторы просто generics-for-namespace, что существенное преимущество этого подход? Классы также могут использоваться как специальные пространства имен с использованием вложенных классы.

Не уверен, что он подходит для значимых, но пространства имен могут быть открыты, а использование классов явно определено.

В целом - я думаю, что нет очевидного "значительного преимущества" функторов в одиночку, это просто другой подход к модуляции кода - стиль ML - и он прекрасно вписывается в основной язык. Не уверен, что сравнение системы модулей, отличных от языка, имеет смысл.

Шаблоны PS С++ и С# generics также совершенно разные, поэтому сравнение с ними в целом кажется немного странным.

Ответ 2

Если я правильно понимаю, функтор дает вам новый набор функций из типа, который вы предоставляете

В более общем плане, функторы сопоставляют модули с модулями. Ваш пример Set сопоставляет модуль, присоединенный к сигнатуре ORDERED_TYPE к модулю, реализующему набор. Подписи ORDERED_TYPE требуется тип и функция сравнения. Поэтому ваш С# не эквивалентен, поскольку он параметризует набор только по типу, а не по сравнению с функцией сравнения. Таким образом, ваш С# может реализовать только один тип набора для каждого типа элемента, тогда как функтор может реализовать множество установленных модулей для каждого элементарного модуля, например. в порядке возрастания и убывания.

Даже если функторы - это просто generics-for-namespace, каково существенное преимущество этого подхода?

Одним из основных преимуществ модульной системы более высокого порядка является способность постепенно совершенствовать интерфейсы. В ООП все является общедоступным или частным (или иногда защищенным или внутренним и т.д.). С модулями вы можете постепенно уточнять подписи модулей по своему желанию, предоставляя более открытый доступ ближе к внутренним модулям и абстрагируясь все больше и больше от него, когда вы будете дальше от этой части кода. Я считаю, что это значительная выгода.

Два примера, когда системы модулей более высокого порядка сравниваются с ООП, параметризуют реализации структуры данных друг над другом и создают расширяемые библиотеки графов. См. Раздел "Структурная абстракция" в Крис Окасаки докторская диссертация для примеров структур данных, параметризованных по сравнению с другими структурами данных, например. функтор, который преобразует очередь в список. См. Отличную библиотеку oamamgraph OCaml и статью Проектирование библиотеки общих графов с использованием ML Functors для примера расширяемых и многоразовых алгоритмов графа с использованием функторов.

Классы также могут использоваться как специальные пространства имен с использованием вложенных классов.

В С# вы не можете параметризовать классы над другими классами. В С++ вы можете сделать некоторые вещи, такие как наследование класса, прошедшего через шаблон.

Кроме того, вы можете использовать функции curry.

Ответ 3

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

Например, в:

structure IntMap1 = MakeMap(Int)
(* ... some other file in some other persons code: *)
structure IntMap2 = MakeMap(Int)

Вы не можете взять карту, созданную функцией в IntMap1, и использовать ее с помощью функции из IntMap2, потому что IntMap1.t - это другой абстрактный тип для IntMap2.t.

На практике это означает, что если в вашей библиотеке есть функция, создающая IntMap.t, то вы также должны предоставить структуру IntMap как часть вашей библиотеки, и если пользователь вашей библиотеки хочет использовать свои собственные (или другие библиотеки) IntMap то он должен преобразовать значения из вашего IntMap в свой IntMap - даже если они уже являются структурно эквивалентными.

Альтернативой является создание библиотеки как самого функтора и требуется, чтобы пользователь библиотеки применил этот функтор с их выбором IntMap. Это также требует, чтобы пользователь библиотеки делал больше работы, чем идеально. Особенно, когда ваша библиотека использует не только IntMap, но и другие виды карт, а также различные наборы и другие.

С generics, OTOH, довольно легко написать библиотеку, создающую карту, и иметь это значение для работы с другими библиотеками, которые принимают Map.

Ответ 4

Я только что нашел источник, который может помочь вам в вашей проблеме - поскольку OCaml имеет другое значение для функторов:

http://books.google.de/books?id=lfTv3iU0p8sC&pg=PA160&lpg=PA160&dq=ocaml+functor&source=bl&ots=vu0sdIB3ja&sig=OhGGcBdaIUR-3-UU05W1VoXQPKc&hl=de&ei=u2e8SqqCNI7R-Qa43IHSCw&sa=X&oi=book_result&ct=result&resnum=9#v=onepage&q=ocaml%20functor&f=false

все еще - я сбиваю с толку, если одно и то же слово используется для разных понятий.


Я не знаю, имеет ли OCaml другое значение, но обычно Functor является "функциональным объектом" (см. здесь: http://en.wikipedia.org/wiki/Function_object). Это полностью отличается от дженериков (см. Здесь http://en.wikipedia.org/wiki/Generic_programming)

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