2009-09-25 11 views
13

Estoy buscando OCaml's functors. Me parece bastante idéntica a los llamados objetos genéricos en C++/C#/Java. Si por ahora ignora la borradura de tipos de Java e ignora los detalles de implementación para las plantillas de C++ (me interesa la función de idioma), los funtores son bastante idénticos a los genéricos. Si he entendido bien, funtor le da un nuevo conjunto de funciones de un tipo que proporciona, de manera que, por ejemplo,Cuál es la diferencia entre functors y "genéricos"

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

Pero más o menos podría reescribir de

#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;; 

en C#

OCaml
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);} 
} 

Claro que hay una ligera diferencia ya que un functor afecta a Module (que es solo un montón de tipos de función y valor es definiciones, similar al espacio de nombre C#).

¿Pero es solo eso? ¿Los funtores son meramente genéricos aplicados a los espacios de nombres? ¿O hay alguna diferencia significativa entre los funtores y los genéricos que me falta?

Incluso si los funtores son solo genéricos para el espacio de nombres, ¿cuál es la ventaja significativa de ese enfoque? Las clases también se pueden usar como espacios de nombres ad-hoc utilizando clases anidadas.

Respuesta

6

¿Pero es solo eso? ¿Los funtores son meramente genéricos aplicados a los espacios de nombres?

Sí, creo que se puede tratar funtores como "espacios de nombres con los genéricos", y que por sí solo habría muy bienvenida en C++, donde la única opción que queda es utilizar las clases con todos los miembros estáticos que se convierte en bastante feo pronto. Comparando con las plantillas de C++, una gran ventaja es la firma explícita en los parámetros del módulo (esto es lo que creo que podrían ser los conceptos de C++ 0x, pero oops).

También los módulos son bastante diferentes de los espacios de nombres (considere múltiples firmas estructurales, tipos abstractos y privados).

Incluso si funtores son sólo genéricos fines de espacio de nombres, ¿cuál es la ventaja significativa de que enfoque? Las clases también se pueden usar como espacios de nombres ad-hoc usando clases anotadas .

No estoy seguro de si califica para significante, pero los espacios de nombres se pueden abrir, mientras que el uso de la clase está explícitamente calificado.

En general, creo que no hay una "ventaja significativa" obvia de los funtores, solo es un enfoque diferente a la modularización de código - estilo ML - y encaja muy bien con el lenguaje de núcleo . No estoy seguro de si comparar el sistema de módulos aparte del idioma tiene mucho sentido.

Las plantillas de PS C++ y los genéricos de C# también son bastante diferentes, por lo que compararlos en su conjunto no tiene nada de extraño.

+0

C++ y C# son diferentes en el aspecto del sistema de tipos? No veo cómo, en la línea inferior tiene un nuevo tipo de lista si es compatible con la máquina virtual o se genera automáticamente en tiempo de compilación. Ilumíname. –

+0

De nada. plantilla clase X { T t; public: int hello() {return t.hello(); } }; clase Hola {public: int hello() {return 1; }}; int main() { X x; return x.hello(); } – ygrek

+0

Tal vez soy un poco denso, pero no lo conseguí. En su ejemplo tiene clase Hola y una clase del tipo X , como en C#. Verifiqué que imprime 1 como se esperaba http://codepad.org/vZUPwFgs cuál es la diferencia en el aspecto del sistema tipo en el ejemplo que proporcionó (por supuesto, usó tipos que no son de referencia y que solo puede usar en C++, pero a excepción de eso no veo la diferencia). –

1

que acaba de encontrar una fuente que puede ayudarle con su problema - como OCaml tiene un significado diferente para funtores:

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

todavía - me resulta confuso si la misma palabra se utiliza para diferentes conceptos .


No sé si OCaml tiene un significado diferente - pero normalmente un Functor es un "objeto Function" (ver aquí: http://en.wikipedia.org/wiki/Function_object). Esto es totalmente diferente de los genéricos (vea aquí: http://en.wikipedia.org/wiki/Generic_programming)

Un objeto de función es un objeto que se puede usar como una función. Los genéricos son una forma de parametrizar objetos. Los genéricos son ortogonales a la herencia (que se especializa en objetos). Los genéricos introducen la seguridad de tipo y reducen la necesidad de lanzar. Los funtores son un puntero de función mejorado.

+1

Functor significa algo diferente en OCaml (que está mucho más cerca de 'genérico' que, por ejemplo, un 'functor' de C++). – Brian

3

Si he entendido bien, funtor le da un nuevo conjunto de funciones de un tipo que proporcione

De manera más general, funtores módulos mapa de módulos. Su ejemplo Set mapea un módulo que se adhiere a la firma ORDERED_TYPE a un módulo que implementa un conjunto. La firma ORDERED_TYPE requiere un tipo y una función de comparación. Por lo tanto, su C# no es equivalente porque parametriza el conjunto solo sobre el tipo y no sobre la función de comparación. Entonces, su C# solo puede implementar un tipo de conjunto para cada tipo de elemento, mientras que el administrador puede implementar muchos módulos de conjunto para cada módulo de elemento, p. en orden ascendente y descendente.

Incluso si los funtores son solo genéricos para el espacio de nombres, ¿cuál es la ventaja significativa de ese enfoque?

Una de las principales ventajas de un sistema de módulo de orden superior es la capacidad de refinar gradualmente las interfaces. En OOP, todo es público o privado (o a veces protegido o interno, etc.). Con los módulos, puede refinar gradualmente las firmas de los módulos a voluntad, brindando más acceso público más cerca de las partes internas de un módulo y abstrayendo más y más de ellas a medida que se aleja de esa parte del código. Encuentro que es un beneficio considerable.

Dos ejemplos donde los sistemas de módulos de mayor orden brillan en comparación con los OOP están parametrizando las implementaciones de la estructura de datos entre sí y construyendo bibliotecas de gráficos extensibles. Consulte la sección sobre "Abstracción estructural" en Chris Okasaki's PhD thesis para ver ejemplos de estructuras de datos parametrizadas sobre otras estructuras de datos, p. un functor que convierte una cola en una lista susceptible de ser almacenada. Consulte la excelente biblioteca de ocamlgraph de OCaml y el documento Designing a Generic Graph Library using ML Functors para ver un ejemplo de algoritmos de gráficos extensibles y reutilizables que utilizan funtores.

Las clases también se pueden usar como espacios de nombres ad-hoc utilizando clases anidadas.

En C#, no puede parametrizar clases sobre otras clases. En C++, puede hacer algunas cosas como heredar de una clase pasada a través de una plantilla.

Además, puede usar funtores de curry.

2

Funcionantes en SML son generativos, por lo que los tipos abstractos producidos por una aplicación de un functor en un punto de un programa no son los mismos que los tipos abstractos producidos por la misma aplicación (es decir, mismo functor, mismo argumento) en otro punto.

Por ejemplo, en:

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

No se puede tomar un mapa producido por una función en IntMap1 y utilizarlo con una función de IntMap2, porque IntMap1.t es un tipo abstracto diferente a IntMap2. t.

En la práctica esto significa que si su biblioteca tiene una función que produce un IntMap.t, también debe suministrar la estructura de IntMap como parte de su biblioteca y si el usuario de su biblioteca desea usar la suya (u otras bibliotecas) IntMap luego tiene que convertir los valores de su IntMap a su IntMap, a pesar de que ya son estructuralmente equivalentes.

La alternativa es hacer que su biblioteca sea un functor, y requerir que el usuario de la biblioteca aplique ese functor con su opción de IntMap. Esto también requiere que el usuario de la biblioteca haga más trabajo que ideal. Especialmente cuando su biblioteca no solo usa IntMap, sino también otros tipos de mapas y varios conjuntos, entre otros.

Con genéricos, OTOH, es bastante fácil escribir una biblioteca que produce un Mapa, y que ese valor funcione con otras funciones de bibliotecas que toman Map.

Cuestiones relacionadas