2009-09-25 1 views
13

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

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#).

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

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

ответ

6

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

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

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

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

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

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

PS C++ шаблоны и C# generics также совершенно разные, так что сравнение с ними в целом кажется немного странным.

+0

C++ и C# отличаются в аспекте системы типов? Я не вижу, как в нижней строке у вас есть новый тип списка , независимо от того, поддерживается ли он виртуальной машиной или автоматически создается во время компиляции. Познай меня. –

+0

Добро пожаловать. описание модели класс X { T t; public: int hello() {return t.hello(); } }; класс Hello {public: int hello() {return 1; }}; int main() { X x; возвращение x.hello(); } – ygrek

+0

Возможно, я немного плотный, но я этого не понял. В вашем примере у вас есть класс Hello и класс типа X , как и в C#. Я подтвердил, что он печатает 1, как ожидалось, http://codepad.org/vZUPwFgs, какая разница в аспекте системы типов в примере, который вы дали (конечно, вы использовали не ссылочные типы, которые вы можете использовать только в C++, но кроме этого я не вижу разницы). –

1

Я только что нашел источник, который может помочь вам с вашей проблемой - в 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)

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

+1

Функтор действительно означает что-то другое в OCaml (это намного ближе к «родовому», чем, например, «функтор C++»). – Brian

3

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

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

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

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

Два примера, когда системы модулей более высокого порядка сравниваются с ООП, параметризуют реализацию структуры данных друг над другом и создают расширяемые библиотеки графов. См. Раздел «Структурная абстракция» в Chris Okasaki's PhD thesis для примеров структур данных, параметризованных по сравнению с другими структурами данных, например. функтор, который преобразует очередь в список. См. Превосходную библиотеку oamamgraph OCaml и документ Designing a Generic Graph Library using ML Functors для примера расширяемых и многоразовых алгоритмов графа с использованием функторов.

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

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

Кроме того, вы можете работать карри.

2

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

Например, в:

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

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

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

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

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