Me he encontrado con el término 'Functor' varias veces mientras leía varios artículos sobre programación funcional, pero los autores generalmente asumen que el lector ya entiende el término. Mirar en la web ha proporcionado descripciones excesivamente técnicas (vea el artículo de Wikipedia ) o descripciones increíblemente vagas (vea la sección sobre Functores en este sitio web ocaml-tutorial ).
¿Puede alguien definir amablemente el término, explicar su uso y tal vez proporcionar un ejemplo de cómo se crean y usan los Functores?
Editar : Si bien estoy interesado en la teoría detrás del término, estoy menos interesado en la teoría que en la implementación y el uso práctico del concepto.
Edición 2 : Parece que está ocurriendo una terminología cruzada: me refiero específicamente a los Functores de la programación funcional, no a los objetos de función de C ++.
fuente
fmap
mapea las funciones. Hay dos tipos de mapeos involucrados. Esa forma de ver las cosas ayudará a comprender la teoría de categorías (que es más general). Quiero decir que es interesante entender la teoría básica de categorías para ayudarnos con todas las cosas de la teoría de categorías en Haskell (functor, mónadas, ...).Respuestas:
La palabra "functor" proviene de la teoría de categorías, que es una rama muy general y muy abstracta de las matemáticas. Ha sido prestado por diseñadores de lenguajes funcionales en al menos dos formas diferentes.
En la familia de idiomas ML, un functor es un módulo que toma uno o más módulos como parámetro. Se considera una función avanzada, y la mayoría de los programadores principiantes tienen dificultades con ella.
Como ejemplo de implementación y uso práctico, podría definir su forma favorita de árbol de búsqueda binaria balanceada de una vez por todas como un functor, y tomaría como parámetro un módulo que proporciona:
El tipo de clave que se utilizará en el árbol binario.
Una función de pedido total en las teclas
Una vez que haya hecho esto, puede usar la misma implementación de árbol binario equilibrado para siempre. (El tipo de valor almacenado en el árbol generalmente se deja polimórfico: el árbol no necesita mirar valores distintos a copiarlos, mientras que el árbol definitivamente necesita poder comparar claves, y obtiene la función de comparación de el parámetro del functor)
Otra aplicación de los functores ML son los protocolos de red en capas . El enlace es a un artículo realmente excelente del grupo CMU Fox; muestra cómo usar los functores para construir capas de protocolo más complejas (como TCP) en tipos de capas más simples (como IP o incluso directamente a través de Ethernet). Cada capa se implementa como un functor que toma como parámetro la capa debajo de ella. La estructura del software en realidad refleja la forma en que las personas piensan sobre el problema, a diferencia de las capas existentes solo en la mente del programador. En 1994, cuando se publicó este trabajo, fue un gran problema.
Para ver un ejemplo salvaje de los functores de ML en acción, puede ver el ML Module Mania en papel , que contiene un ejemplo publicable (es decir, aterrador) de functors en el trabajo. Para una explicación brillante, clara y lúcida del sistema de módulos ML (con comparaciones con otros tipos de módulos), lea las primeras páginas del brillante papel POPL 1994 de Xavier Leroy Tipos de Manifiesto, Módulos y Compilación Separada .
En Haskell, y en algún lenguaje funcional puro relacionado,
Functor
hay una clase de tipo . Un tipo pertenece a una clase de tipo (o más técnicamente, el tipo "es una instancia de" la clase de tipo) cuando el tipo proporciona ciertas operaciones con cierto comportamiento esperado. Un tipoT
puede pertenecer a la claseFunctor
si tiene cierto comportamiento de colección:El tipo
T
se parametriza sobre otro tipo, que debe considerar como el tipo de elemento de la colección. El tipo de la colección completa es entonces algo así comoT Int
,T String
,T Bool
, si se contiene números enteros, cadenas o Booleanos respectivamente. Si el tipo de elemento es desconocido, se escribe como un parámetro de tipoa
, como enT a
.Los ejemplos incluyen listas (cero o más elementos de tipo
a
), elMaybe
tipo (cero o uno de los elementos de tipoa
), conjuntos de elementos de tipoa
, matrices de elementos de tipoa
, todo tipo de árboles de búsqueda que contienen valores de tipoa
y muchos otros se me ocurre.La otra propiedad que
T
tiene que satisfacer es que si tiene una función de tipoa -> b
(una función en elementos), entonces debe poder tomar esa función y producir una función relacionada en colecciones. Lo hace con el operadorfmap
, que es compartido por cada tipo en laFunctor
clase de tipo. El operador está realmente sobrecargado, por lo que si tiene una funcióneven
con tipoInt -> Bool
, entonceses una función sobrecargada que puede hacer muchas cosas maravillosas:
Convierta una lista de enteros en una lista de booleanos
Convierta un árbol de enteros en un árbol de booleanos
Convertir
Nothing
aNothing
yJust 7
paraJust False
En Haskell, esta propiedad se expresa dando el tipo de
fmap
:donde ahora tenemos un pequeño
t
, que significa "cualquier tipo en laFunctor
clase".Para resumir, en Haskell un functor es un tipo de colección para la que si se le asigna una función sobre elementos,
fmap
le devolverá una función sobre colecciones . Como puede imaginar, esta es una idea que puede reutilizarse ampliamente, por lo que es bendecida como parte de la biblioteca estándar de Haskell.Como de costumbre, las personas continúan inventando nuevas abstracciones útiles, y es posible que desee buscar functores aplicativos , para los cuales la mejor referencia puede ser un artículo llamado Programación Aplicativa con Efectos por Conor McBride y Ross Paterson.
fuente
then you have to be able to take that function and product a related function on collections
¿Quiso decir enproduce
lugar deproduct
?Otras respuestas aquí están completas, pero intentaré otra explicación del uso de FP del functor . Toma esto como analogía:
A diferencia del uso del puntero de función abstraída en C ++, aquí el functor no es la función; más bien, es algo que se comporta de manera consistente cuando está sujeto a una función .
fuente
¡Hay tres significados diferentes, no muy relacionados!
En Ocaml es un módulo parametrizado. Ver manual . Creo que la mejor manera de asimilarlos es con un ejemplo: (escrito rápidamente, podría tener errores)
Ahora puede agregar rápidamente muchas órdenes posibles, formas de formar nuevas órdenes, hacer una búsqueda binaria o lineal fácilmente sobre ellas. Programación genérica FTW.
En lenguajes de programación funcionales como Haskell, significa algunos constructores de tipos (tipos parametrizados como listas, conjuntos) que se pueden "mapear". Para ser precisos, un functor
f
está equipado con(a -> b) -> (f a -> f b)
. Esto tiene orígenes en la teoría de categorías. El artículo de Wikipedia al que se vinculó es este uso.Por lo tanto, este es un tipo especial de constructores tipo, ¡y tiene poco que ver con los functors en Ocaml!
fuente
En OCaml, es un módulo parametrizado.
Si conoce C ++, piense en un functor OCaml como una plantilla. C ++ solo tiene plantillas de clase, y los functores trabajan en la escala del módulo.
Un ejemplo de functor es Map.Make;
module StringMap = Map.Make (String);;
crea un módulo de mapa que funciona con mapas con clave de cadena.No podría lograr algo como StringMap con solo polimorfismo; necesita hacer algunas suposiciones sobre las teclas. El módulo String contiene las operaciones (comparación, etc.) en un tipo de cadena totalmente ordenado, y el functor se vinculará con las operaciones que contiene el módulo String. Podría hacer algo similar con la programación orientada a objetos, pero tendría una sobrecarga indirecta del método.
fuente
Tienes bastantes buenas respuestas. Voy a lanzar:
Un functor, en el sentido matemático, es un tipo especial de función en un álgebra. Es una función mínima que asigna un álgebra a otro álgebra. La "minimidad" se expresa en las leyes de los functores.
Hay dos maneras de ver esto. Por ejemplo, las listas son functores sobre algún tipo. Es decir, dado un álgebra sobre un tipo 'a', puede generar un álgebra compatible de listas que contengan elementos del tipo 'a'. (Por ejemplo: el mapa que lleva un elemento a una lista singleton que lo contiene: f (a) = [a]) Nuevamente, la noción de compatibilidad se expresa mediante las leyes de los functores.
Por otro lado, dado un functor f "sobre" un tipo a, (es decir, fa es el resultado de aplicar el functor f al álgebra del tipo a), y la función de g: a -> b, podemos calcular un nuevo functor F = (fmap g) que asigna fa a f b. En resumen, fmap es la parte de F que asigna "partes functoras" a "partes functoras", y g es la parte de la función que asigna "partes algebraicas" a "partes algebraicas". Se necesita una función, un functor, y una vez completado, también es un functor.
Puede parecer que diferentes idiomas están utilizando diferentes nociones de functores, pero no lo son. Simplemente están utilizando functores sobre diferentes álgebras. OCamls tiene un álgebra de módulos, y los functores sobre ese álgebra le permiten adjuntar nuevas declaraciones a un módulo de manera "compatible".
Un funk Haskell NO es una clase de tipo. Es un tipo de datos con una variable libre que satisface la clase de tipo. Si está dispuesto a profundizar en las entrañas de un tipo de datos (sin variables libres), puede reinterpretar un tipo de datos como un functor sobre un álgebra subyacente. Por ejemplo:
datos F = F Int
es isomorfo a la clase de Ints. Entonces F, como un constructor de valores, es una función que asigna Int a F Int, un álgebra equivalente. Es un functor. Por otro lado, no obtienes fmap gratis aquí. Para eso sirve la coincidencia de patrones.
Los functores son buenos para "unir" cosas a elementos de álgebras, de una manera algebraicamente compatible.
fuente
La mejor respuesta a esa pregunta se encuentra en "Typeclassopedia" de Brent Yorgey.
Este número de Monad Reader contiene una definición precisa de lo que es un functor, así como muchas definiciones de otros conceptos y un diagrama. (Monoid, Applicative, Monad y otros conceptos se explican y se ven en relación con un functor).
http://haskell.org/sitewiki/images/8/85/TMR-Issue13.pdf
Extracto de Typeclassopedia for Functor: "Una intuición simple es que un Functor representa un" contenedor "de algún tipo, junto con la capacidad de aplicar una función uniformemente a cada elemento en el contenedor"
Pero realmente toda la biblioteca de tipos es una lectura altamente recomendada que es sorprendentemente fácil. En cierto modo, puede ver la clase de tipo presentada allí como un patrón paralelo al diseño en el objeto en el sentido de que le dan un vocabulario para el comportamiento o la capacidad dada.
Salud
fuente
Hay un muy buen ejemplo en el libro O'Reilly OCaml que se encuentra en el sitio web de Inria (que al momento de escribir esto lamentablemente no funciona). Encontré un ejemplo muy similar en este libro utilizado por caltech: Introducción a OCaml (enlace pdf) . La sección relevante es el capítulo sobre functores (página 139 en el libro, página 149 en el PDF).
En el libro tienen un functor llamado MakeSet que crea una estructura de datos que consiste en una lista y funciones para agregar un elemento, determinar si un elemento está en la lista y encontrar el elemento. La función de comparación que se usa para determinar si está o no en el conjunto se ha parametrizado (que es lo que hace que MakeSet sea un functor en lugar de un módulo).
También tienen un módulo que implementa la función de comparación para que haga una comparación de cadenas sin distinción entre mayúsculas y minúsculas.
Usando el functor y el módulo que implementa la comparación, pueden crear un nuevo módulo en una línea:
que crea un módulo para una estructura de datos establecida que utiliza comparaciones que no distinguen entre mayúsculas y minúsculas. Si quisiera crear un conjunto que utilizara comparaciones entre mayúsculas y minúsculas, simplemente necesitaría implementar un nuevo módulo de comparación en lugar de un nuevo módulo de estructura de datos.
Tobu comparó los functores con las plantillas en C ++, lo que creo que es bastante adecuado.
fuente
Dadas las otras respuestas y lo que voy a publicar ahora, diría que es una palabra bastante sobrecargada, pero de todos modos ...
Para obtener una pista sobre el significado de la palabra 'functor' en Haskell, pregunte a GHCi:
Entonces, básicamente, un functor en Haskell es algo que se puede mapear. Otra forma de decirlo es que un functor es algo que puede considerarse como un contenedor al que se le puede pedir que use una función dada para transformar el valor que contiene; Por lo tanto, para las listas,
fmap
coincide conmap
, paraMaybe
,fmap f (Just x) = Just (f x)
,fmap f Nothing = Nothing
etc.La subsección de la clase de tipo Functor y la sección Functores, Functores aplicativos y Monoides de Learn You a Haskell for Great Good dan algunos ejemplos de dónde es útil este concepto en particular. (Un resumen: ¡muchos lugares! :-))
Tenga en cuenta que cualquier mónada puede ser tratada como un functor, y de hecho, como señala Craig Stuntz, los functores más utilizados tienden a ser mónadas ... OTOH, a veces es conveniente hacer que un tipo sea una instancia de la clase de tipo Functor sin molestarse en convertirlo en una mónada. (Por ejemplo, en el caso de
ZipList
fromControl.Applicative
, mencionado en una de las páginas mencionadas anteriormente ).fuente
Aquí hay un artículo sobre functores de un POV de programación , seguido más específicamente de cómo aparecen en los lenguajes de programación .
El uso práctico de un functor es en una mónada, y puede encontrar muchos tutoriales sobre mónadas si lo busca.
fuente
En un comentario a la respuesta más votada , el usuario Wei Hu pregunta:
Nota : No sé ML, así que perdona y corrige los errores relacionados.
Asumamos inicialmente que todos estamos familiarizados con las definiciones de 'categoría' y 'functor'.
Una respuesta compacta sería que los "Haskell-functors" son (endo-) functors
F : Hask -> Hask
mientras que los "ML-functors" son functorsG : ML -> ML'
.Aquí,
Hask
es la categoría formada por los tipos y funciones de Haskell entre ellos, y de manera similarML
yML'
son categorías definidas por las estructuras de ML.Nota : Existen algunos problemas técnicos con la creación de
Hask
una categoría, pero hay formas de evitarlos.Desde una perspectiva teórica de categoría, esto significa que un
Hask
-functor es un mapaF
de tipos de Haskell:junto con un mapa
fmap
de funciones de Haskell:ML es más o menos lo mismo, aunque no
fmap
conozco ninguna abstracción canónica , así que definamos una:Eso es
f
mapasML
-tipos yfmap
mapasML
-funciones, entoncesEs functor
F: StructA -> StructB
.fuente
"Functor es el mapeo de objetos y morfismos que preserva la composición e identidad de una categoría".
Vamos a definir qué es una categoría?
¿Qué tiene la categoría?
Entonces, tenemos que mapear los objetos y preservar la composición después de aplicar nuestro Functor.
Imaginemos que 'A' es nuestra categoría que tiene objetos ['a', 'b'] y existe un morfismo a -> b
Ahora, tenemos que definir un functor que pueda mapear estos objetos y morfismos en otra categoría 'B'.
Digamos que el functor se llama 'Quizás'
Entonces, la categoría 'B' se ve así.
Dibuje otro círculo pero esta vez con 'Quizás a' y 'Quizás b' en lugar de 'a' y 'b'.
Todo parece bien y todos los objetos están mapeados
'a' se convirtió en 'Quizás a' y 'b' se convirtió en 'Quizás b'.
Pero el problema es que también tenemos que mapear el morfismo de 'a' a 'b'.
Eso significa que el morfismo a -> b en 'A' debería corresponder con el morfismo 'Quizás a' -> 'Quizás b'
el morfismo de a -> b se llama f, luego el morfismo de 'Quizás a' -> 'Quizás b' se llama 'fmap f'
Ahora veamos qué función estaba haciendo 'f' en 'A' y veamos si podemos replicarlo en 'B'
definición de función de 'f' en 'A':
f toma a y devuelve b
definición de función de 'f' en 'B':
f toma Quizás a y devuelve Quizás b
veamos cómo usar fmap para mapear la función 'f' de 'A' a la función 'fmap f' en 'B'
definición de fmap
Entonces que hacemos aqui ?
Estamos aplicando la función 'f' a 'x' que es de tipo 'a'. La coincidencia de patrón especial de 'Nothing' proviene de la definición de
Functor Maybe
.Entonces, mapeamos nuestros objetos [a, b] y morfismos [f] de la categoría 'A' a la categoría 'B'.
Eso es Functor!
fuente
Descripción general
En la programación funcional, un functor es esencialmente una construcción de levantar funciones unarias ordinarias (es decir, aquellas con un argumento) para funciones entre variables de nuevos tipos. Es mucho más fácil escribir y mantener funciones simples entre objetos simples y usar functores para levantarlos, luego escribir funciones manualmente entre objetos contenedores complicados. Otra ventaja es escribir funciones simples solo una vez y luego reutilizarlas a través de diferentes functores.
Los ejemplos de functores incluyen matrices, functores "tal vez" y "cualquiera", futuros (ver, por ejemplo, https://github.com/Avaq/Fluture ) y muchos otros.
Ilustración
Considere la función que construye el nombre completo de la persona a partir del nombre y apellido. Podríamos definirlo
fullName(firstName, lastName)
como una función de dos argumentos, que, sin embargo, no sería adecuado para los functors que solo se ocupan de las funciones de un argumento. Para remediar, recopilamos todos los argumentos en un solo objetoname
, que ahora se convierte en el argumento único de la función:Ahora, ¿qué pasa si tenemos muchas personas en una matriz? En lugar de repasar manualmente la lista, simplemente podemos reutilizar nuestra función a
fullName
través delmap
método proporcionado para matrices con una sola línea de código corta:y úsalo como
Eso funcionará, siempre que cada entrada en nuestro
nameList
sea un objeto que proporcione propiedadesfirstName
ylastName
propiedades. Pero, ¿qué pasa si algunos objetos no lo hacen (o incluso no son objetos)? Para evitar los errores y hacer que el código sea más seguro, podemos envolver nuestros objetos en elMaybe
tipo (por ejemplo, https://sanctuary.js.org/#maybe-type ):donde
Just(name)
es un contenedor que lleva solo nombres válidos yNothing()
es el valor especial utilizado para todo lo demás. Ahora, en lugar de interrumpir (u olvidar) para verificar la validez de nuestros argumentos, simplemente podemos reutilizar (levantar) nuestrafullName
función original con otra sola línea de código, basado nuevamente en elmap
método, esta vez provisto para el tipo Quizás:y úsalo como
Teoría de la categoría
Un Functor en la teoría de categorías es un mapa entre dos categorías que respeta la composición de sus morfismos. En un lenguaje de computadora , la categoría principal de interés es aquella cuyos objetos son tipos (ciertos conjuntos de valores) y cuyos morfismos son funciones
f:a->b
de un tipoa
a otrob
.Por ejemplo, tome
a
como elString
tipo,b
el tipo Número yf
es la función que asigna una cadena a su longitud:Aquí
a = String
representa el conjunto de todas las cadenas yb = Number
el conjunto de todos los números. En ese sentido, tantoa
yb
representar objetos en la categoría de conjunto (que está estrechamente relacionado con la categoría de tipos, con la diferencia de que aquí no esencial). En la categoría de conjunto, los morfismos entre dos conjuntos son, precisamente, todas las funciones desde el primer conjunto hasta el segundo. Entonces, nuestra función de longitudf
aquí es un morfismo del conjunto de cadenas en el conjunto de números.Como solo consideramos la categoría de conjunto, los Functores relevantes de ella en sí mismos son mapas que envían objetos a objetos y morfismos a morfismos, que satisfacen ciertas leyes algebraicas.
Ejemplo:
Array
Array
puede significar muchas cosas, pero solo una es Functor: la construcción de tipos, que asigna un tipoa
al tipo[a]
de todas las matrices de tiposa
. Por ejemplo, elArray
functor asigna el tipoString
al tipo[String]
(el conjunto de todas las matrices de cadenas de longitud arbitraria) y establece el tipoNumber
en el tipo correspondiente[Number]
(el conjunto de todas las matrices de números).Es importante no confundir el mapa Functor
con un morfismo
a -> [a]
. El functor simplemente asigna (asocia) el tipoa
en el tipo[a]
como una cosa a otra. Que cada tipo es en realidad un conjunto de elementos, no tiene relevancia aquí. Por el contrario, un morfismo es una función real entre esos conjuntos. Por ejemplo, hay un morfismo natural (función)que envía un valor a la matriz de 1 elemento con ese valor como entrada única. ¡Esa función no es parte del
Array
Functor! Desde el punto de vista de este functor,pure
es solo una función como cualquier otra, nada especial.Por otro lado, el
Array
Functor tiene su segunda parte: la parte del morfismo. Que mapea un morfismof :: a -> b
en un morfismo[f] :: [a] -> [b]
:Aquí
arr
hay cualquier conjunto de longitud arbitraria con valores de tipoa
, yarr.map(f)
es el conjunto de la misma longitud con valores de tipob
, cuyas entradas son resultados de aplicarf
a las entradas dearr
. Para convertirlo en un functor, deben cumplir las leyes matemáticas de mapeo de identidad a identidad y de composiciones a composiciones, que son fáciles de verificar en esteArray
ejemplo.fuente
No para contradecir las respuestas teóricas o matemáticas anteriores, pero un Functor también es un Objeto (en un lenguaje de programación Orientado a Objetos) que tiene un solo método y se usa efectivamente como una función.
Un ejemplo es la interfaz Runnable en Java, que tiene un solo método: ejecutar.
Considere este ejemplo, primero en Javascript, que tiene funciones de primera clase:
Salida: [1, 4, 25, 100]
El método map toma una función y devuelve una nueva matriz con cada elemento como resultado de la aplicación de esa función al valor en la misma posición en la matriz original.
Para hacer lo mismo es Java, utilizando un Functor, primero debe definir una interfaz, por ejemplo:
Luego, si agrega una clase de colección que tenía una función de mapa, podría hacer:
Esto utiliza una subclase en línea de IntMapFunction para crear un Functor, que es el equivalente OO de la función del ejemplo anterior de JavaScript.
El uso de Functors le permite aplicar técnicas funcionales en un lenguaje OO. Por supuesto, algunos lenguajes OO también tienen soporte para funciones directamente, por lo que no es necesario.
Referencia: http://en.wikipedia.org/wiki/Function_object
fuente
Array
es un functor, peroArray(value)
solo proporciona matrices de 1 elemento.BESO: Un functor es un objeto que tiene un método de mapa.
Las matrices en JavaScript implementan el mapa y, por lo tanto, son functores. Promises, Streams and Trees a menudo implementan mapas en lenguajes funcionales, y cuando lo hacen, se les considera functores. El método de mapa del functor toma sus propios contenidos y transforma cada uno de ellos utilizando la devolución de llamada de transformación pasada al mapa, y devuelve un nuevo functor, que contiene la estructura como el primer functor, pero con los valores transformados.
src: https://www.youtube.com/watch?v=DisD9ftUyCk&feature=youtu.be&t=76
fuente
Array
La construcción tipo define un solo functor. Sus instancias también se denominan "matrices" pero no son functores. La descripción aquí debe hacerse más precisa.En la práctica, functor significa un objeto que implementa el operador de llamada en C ++. En ocaml creo que functor se refiere a algo que toma un módulo como entrada y salida de otro módulo.
fuente
En pocas palabras, un functor u objeto de función es un objeto de clase que se puede llamar como una función.
En C ++:
Así es como escribes una función
Así es como escribes un functor
Ahora puedes hacer esto:
Lo que los hace tan geniales es que puedes mantener el estado en la clase; imagina si quisieras preguntarle a una función cuántas veces se ha llamado. No hay forma de hacer esto de una manera ordenada y encapsulada. Con un objeto de función, es como cualquier otra clase: tendría alguna variable de instancia en la que incremente
operator ()
y algún método para inspeccionar esa variable, y todo está ordenado como desee.fuente
FunctorClass
cumple con la primera Ley del Fundador, pero ¿podría esbozar una prueba para la segunda Ley? No lo veo del todo.Functor no está específicamente relacionado con la programación funcional. Es solo un "puntero" a una función o algún tipo de objeto, que se puede llamar como sería una función.
fuente