2010-03-03 8 views
15

Powiel możliwe:
In Functional Programming, what is a functor?Czy mógłbyś mi wyjaśnić funciaki OCaml?

nie wiem zbyt wiele o SML, Uczyłem F # od jakiegoś czasu i bardzo to rozumiem.

Mówią, że F # tęskni za modelem funktora, który jest obecny w OCaml. Próbowałem dowiedzieć się, czym właściwie jest funktor, ale wikipedia i tutoriale mi nie pomogły.

Czy możesz mi wyjaśnić tę tajemnicę? Z góry dzięki :)

EDIT:

Złapałem punkt, thx wszystkim, którzy mi pomogli. Możesz zamknąć pytanie jako dokładny duplikat: In Functional Programming, what is a functor?

+0

Nie jestem F # ani OCaml guru więc wygrał Zrób to jako formalną odpowiedź, ale myślę, że http://blog.matthewdoig.com/?p=152 może ci pomóc w wyjaśnianiu funktorów i jak F # radzi sobie z ich nieobecnością. –

+0

Przeczytałem najpierw ten artykuł, jest też blog.matthewdoig.com/?p=155. Jestem w połowie drogi do zrozumienia tej rzeczy :) – Bubba88

Odpowiedz

12

Funktory to moduły sparametryzowane przez moduły, tj. Odbicie od modułów do modułów (zwykłą funkcją jest odbicie od wartości do wartości, funkcja polimorficzna jest odbiciem od typów do zwykłych funkcji).

Zobacz także ocaml-tutorial on modules. Pomocne są również

Examples in the manual.

+0

Spóźniłem się 10 sekund :-) Nie jestem specjalistą i zrozumiałem to wyjaśnienie. – yogsototh

+2

To jest dobre wytłumaczenie tego, czym są funfery OCaml * i * czym one * są dobre dla * są programowanie ogólnych struktur danych (zobacz implementację Seta w standardowej bibliotece OCaml, jest to prawdopodobnie najprostszy przykład funktora) i ogólnie oferuje wydajne, sprawdzone statycznie, alternatywne rozwiązanie wielu problemów, które można rozwiązać za pomocą programowania obiektowego. –

+0

Chciałbym mieć to szczęście) .. Więc, jeśli dobrze to rozumiem: funktory są sposobem na tworzenie nowych modułów poprzez parametryzowanie istniejących modułów, takich jak 'Set'? – Bubba88

29

Jeśli pochodzą z wszechświat OOP, to prawdopodobnie pomaga myśleć o module analogicznym do klasy statycznej. Podobne do klas statycznych .NET, moduł OCaml ma konstruktory; w przeciwieństwie do .NET, moduły OCaml mogą akceptować parametry w swoich konstruktorach. Funktor to przerażająco brzmiąca nazwa obiektu, który przekazujesz do konstruktora modułu.

Więc pomocą kanoniczny przykład binarnego drzewa, my normalnie napisać go w F # tak:

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) 

cacy. Ale w jaki sposób F # wie, jak porównać dwa obiekty typu 'a używając operatorów < i >?

Za kulisami jej robi coś takiego:

> let gt x y = x > y;; 

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

Dobrze, dobrze, co jeśli masz obiektu typu Person który nie wykona tego konkretnego interfejsu? Co jeśli chcesz zdefiniować funkcję sortowania w locie?Jednym ze sposobów jest po prostu przejść w comparer następująco:

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) 

To działa, ale jeśli piszesz moduł do operacji drzewa z wkładką, Lookup, usuwanie, itp, wymagają klienci przechodzą w funkcja zamawiania za każdym razem, gdy wywołują cokolwiek.

Jeżeli F # obsługiwane funktory, jego składnia hipotetyczny może wyglądać następująco:

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) 

Jeszcze w składni hipotetycznej, można by utworzyć moduł jako taki:

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 

Niestety, F # nie robi wspierają funktory, więc musicie oprzeć się pewnym nieuporządkowanym obejściom. Dla scenariusza powyżej, ja prawie zawsze przechowywać „funktor” w moim struktury danych z niektórych AUXILLARY funkcji pomocniczych, aby upewnić się, że zostanie skopiowany wokół poprawnie:

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)) 
+4

Dziękujemy za pomocny przykład. Udało ci się to zrobić bez linii kodu Ocaml, huh)) – Bubba88

+4

Czym to się różni od przyjmowania instancji IEqualityComparer, na przykład w konstruktorze? Można go zaimplementować ad-hoc za pomocą wyrażeń obiektowych. – Daniel

+6

@Daniel: w gruncie rzeczy chodzi o sedno. Funkcjonariusz OCaml to w zasadzie to samo, co przekazanie jakiegoś obiektu do konstruktora modułu. Ale ponieważ klasy F # modules == .NET statyczne i konstruktory statyczne .NET nie akceptują parametrów w swoich konstruktorach, musisz przeskakiwać przez obręcze, aby uzyskać ten sam rodzaj funkcjonalności. – Juliet