Какая разница между функторами и "дженериками",
Я смотрю 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)
Функциональный объект - это объект, который может использоваться как функция. Дженерики - это способ параметризации объектов. Дженерики являются ортогональными для наследования (которые специализируют объекты). Дженерики вводят типы безопасности и уменьшают потребность в литье. Функторы - улучшенный указатель функции.