functional programming - programming - En Programación Funcional, ¿qué es un functor?
programacion funcional funciones (17)
Me he topado 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 alrededor en la web ha proporcionado descripciones excesivamente técnicas (consulte el artículo de Wikipedia ) o descripciones increíblemente vagas (consulte la sección sobre Functors en este sitio web de tutorial de ocaml ).
¿Puede alguien definir amablemente el término, explicar su uso y quizás proporcionar un ejemplo de cómo se crean y utilizan los Functors?
Edit : Aunque 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 hay algo de terminología cruzada en curso: me refiero específicamente a los Functors de la programación funcional, no a los objetos de función de C ++.
Descripción general
En la programación funcional, un funtor 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 funtores para levantarlos, y 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 funtores.
Los ejemplos de functores incluyen matrices, "tal vez" y "cualquiera" de los funtores, 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 desde los nombres y apellidos. Podríamos definirlo como fullName(firstName, lastName)
como función de dos argumentos, que sin embargo no sería adecuado para los funtores que solo se ocupan de las funciones de un argumento. Para remediarlo, recopilamos todos los argumentos en un solo name
objeto, que ahora se convierte en el único argumento de la función:
// In JavaScript notation
fullName = name => name.firstName + '' '' + name.lastName
Ahora, ¿qué pasa si tenemos muchas personas en una matriz? En lugar de revisar manualmente la lista, simplemente podemos reutilizar nuestra función fullName
través del método de map
provisto para matrices con una sola línea de código:
fullNameList = nameList => nameList.map(fullName)
y usalo como
nameList = [
{firstName: ''Steve'', lastName: ''Jobs''},
{firstName: ''Bill'', lastName: ''Gates''}
]
fullNames = fullNameList(nameList)
// => [''Steve Jobs'', ''Bill Gates'']
Eso funcionará, siempre que cada entrada en nuestra lista de nameList
sea un objeto que proporcione las firstName
y lastName
. 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 el tipo Maybe
(véase, por ejemplo, https://sanctuary.js.org/#maybe-type ):
// function to test name for validity
isValidName = name =>
(typeof name === ''object'')
&& (typeof name.firstName === ''string'')
&& (typeof name.lastName === ''string'')
// wrap into the Maybe type
maybeName = name =>
isValidName(name) ? Just(name) : Nothing()
donde Just(name)
es un contenedor que contiene solo nombres válidos y Nothing()
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) nuestra función fullName
original con otra línea de código única, basada nuevamente en el método del map
, esta vez para el tipo Maybe:
// Maybe Object -> Maybe String
maybeFullName = maybeName => maybeName.map(fullName)
y usalo como
justSteve = maybeName(
{firstName: ''Steve'', lastName: ''Jobs''}
) // => Just({firstName: ''Steve'', lastName: ''Jobs''})
notSteve = maybeName(
{lastName: ''SomeJobs''}
) // => Nothing()
steveFN = maybeFullName(justSteve)
// => Just(''Steve Jobs'')
notSteveFN = maybeFullName(notSteve)
// => Nothing()
Categoria teoria
Un Functor en la teoría de categorías es un mapa entre dos categorías que respetan la composición de sus morfismos. En un lenguaje informático , la principal categoría de interés es aquella cuyos objetos son tipos (ciertos conjuntos de valores) y cuyos morfismos son funciones f:a->b
de un tipo a otro tipo b
.
Por ejemplo, tome a
para ser el tipo de String
, b
el tipo de número y f
es la función que asigna una cadena a su longitud:
// f :: String -> Number
f = str => str.length
Aquí a = String
representa el conjunto de todas las cadenas y b = Number
el conjunto de todos los números. En ese sentido, tanto a
como b
representan objetos en la Categoría de Conjunto (que está estrechamente relacionada con la categoría de tipos, con la diferencia que aquí no es esencial). En la categoría de conjuntos, los morfismos entre dos conjuntos son precisamente todas las funciones desde el primer conjunto hasta el segundo. Así que nuestra función de longitud f
aquí es un morfismo del conjunto de cadenas al conjunto de números.
Como solo consideramos la categoría de conjuntos, los Functors 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 cosa es un Functor: el tipo de construcción, mapeando un tipo a
en el tipo [a]
de todos los arreglos de tipo a
. Por ejemplo, el functor Array
asigna el tipo String
al tipo [String]
(el conjunto de todos los arreglos de cadenas de longitud arbitraria), y establece el Number
tipo en el tipo correspondiente [Number]
(el conjunto de todos los arreglos de números).
Es importante no confundir el mapa de Functor
Array :: a => [a]
con un morfismo a -> [a]
. El functor simplemente mapea (asocia) el tipo a
en el tipo [a]
como una cosa a otra. Que cada tipo es en realidad un conjunto de elementos, no es de relevancia aquí. En contraste, un morfismo es una función real entre esos conjuntos. Por ejemplo, hay un morfismo natural (función)
pure :: a -> [a]
pure = x => [x]
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 funtor, 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 morfismo f :: a -> b
en un morfismo [f] :: [a] -> [b]
:
// a -> [a]
Array.map(f) = arr => arr.map(f)
Aquí arr
es cualquier matriz de longitud arbitraria con valores de tipo a
, y arr.map(f)
es la matriz de la misma longitud con valores de tipo b
, cuyas entradas son el resultado de aplicar f
a las entradas de arr
. Para convertirlo en un funtor, las leyes matemáticas del mapeo de identidad a identidad y las composiciones a composiciones deben mantenerse, que son fáciles de verificar en este ejemplo de Array
.
KISS: Un functor es un objeto que tiene un método de mapa.
Las matrices en JavaScript implementan el mapa y, por lo tanto, son funtores. Promises, Streams y Trees a menudo implementan mapas en lenguajes funcionales, y cuando lo hacen, se consideran funtores. 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 que se pasa 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
"Functor es un mapeo de objetos y morfismos que preserva la composición y la identidad de una categoría".
Vamos a definir qué es una categoría?
¡Es un montón de objetos!
Dibuje algunos puntos (por ahora 2 puntos, uno es ''a'' otro es ''b'') dentro de un círculo y nombre ese círculo A (Categoría) por ahora.
¿Qué sostiene la categoría?
Composición entre objetos y función de identidad para cada objeto.
Por lo tanto, 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 asignar estos objetos y morfismos a otra categoría ''B''.
Digamos que el funtor se llama ''Tal vez''
data Maybe a = Nothing | Just a
Por lo tanto, la categoría ''B'' se ve así.
Dibuja otro círculo, pero esta vez con ''Tal vez a'' y ''Tal vez b'' en lugar de ''a'' y ''b''.
Todo parece bien y todos los objetos están mapeados.
''a'' se convirtió en ''Tal vez a'' y ''b'' se convirtió en ''Tal vez b''.
Pero el problema es que tenemos que mapear el morfismo de ''a'' a ''b'' también.
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 ''Tal vez a'' -> ''Tal vez b'' se llama ''fmap f''
Ahora veamos qué función ''f'' estaba haciendo en ''A'' y veamos si podemos replicarla en ''B''
Definición de la función de ''f'' en ''A'':
f :: a -> b
f toma a y devuelve b
Definición de la función de ''f'' en ''B'':
f :: Maybe a -> Maybe b
f toma Tal vez una y regreso Tal vez 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
fmap :: (a -> b) -> (Maybe a -> Maybe b)
fmap f Nothing = Nothing
fmap f (Just x) = Just(f x)
Entonces que hacemos aqui ?
Estamos aplicando la función ''f'' a ''x'' que es de tipo ''a''. La coincidencia de patrones especiales de ''Nada'' 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!
Aquí hay un artículo sobre los funtores 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 puedes encontrar muchos tutoriales en mónadas si buscas eso.
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:
Prelude> :info Functor
class Functor f where
fmap :: forall a b. (a -> b) -> f a -> f b
(GHC.Base.<$) :: forall a b. a -> f b -> f a
-- Defined in GHC.Base
instance Functor Maybe -- Defined in Data.Maybe
instance Functor [] -- Defined in GHC.Base
instance Functor IO -- Defined in GHC.Base
Entonces, básicamente, un functor en Haskell es algo que puede ser mapeado. 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 determinada para transformar el valor que contiene; por lo tanto, para listas, fmap
coincide con map
, para Maybe
, fmap f (Just x) = Just (fx)
, fmap f Nothing = Nothing
etc.
La subsección de clases de tipos de Functor y la sección sobre Functors, Applicative Functors y Monoids of 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 tratarse como un funtor y, de hecho, como señala Craig Stuntz, los funtores más utilizados suelen ser las mónadas ... OTOH, a veces es conveniente hacer que un tipo sea una instancia de la clase de tipos de Functor Sin tomarse la molestia de convertirlo en una mónada. (Por ejemplo, en el caso de ZipList
de Control.Applicative
, mencionado en una de las páginas mencionadas anteriormente ).
En OCaml, es un módulo parametrizado.
Si conoces C ++, piensa en un functor OCaml como una plantilla. C ++ sólo se dispone de plantillas de clase, y funtores trabajan en la escala módulo.
Un ejemplo de functor es Map.Make; module StringMap = Map.Make (String);;
construye un módulo de mapa que funciona con mapas de clave de cadena.
No se podía lograr algo así como stringmap con sólo polimorfismo; Necesitas hacer algunas suposiciones sobre las claves. 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 de direccionamiento indirecto de método.
En un comentario a la parte superior votado answer , el usuario Wei Hu pide:
Entiendo tanto a los ML-functors como a los Haskell-functors, pero me falta la idea para relacionarlos. ¿Cuál es la relación entre estos dos, en un sentido teórico-categoría?
Nota : No sé ML, así que perdona y corrige cualquier error relacionado.
Supongamos inicialmente que todos estamos familiarizados con las definiciones de ''categoría'' y ''functor''.
Una respuesta compacta sería que los "Haskell-functors" son (endo) funtores F : Hask -> Hask
mientras que "ML-functors" son los funtores G : ML -> ML''
.
Aquí, Hask
es la categoría formada por los tipos y funciones de Haskell entre ellos, y de manera similar, ML
y ML''
son categorías definidas por las estructuras de ML.
Nota : hay algunos problemas técnicos para hacer que Hask
una categoría, pero existen formas de Hask
.
Desde una perspectiva teórica de categorías, esto significa que un Hask
-functor es un mapa F
de tipos Haskell:
data F a = ...
junto con un mapa fmap
de funciones de Haskell:
instance Functor F where
fmap f = ...
ML es prácticamente igual, aunque no fmap
abstracción de fmap
canónica, así que definamos una:
signature FUNCTOR = sig
type ''a f
val fmap: ''a -> ''b -> ''a f -> ''b f
end
Eso es f
maps ML
type, y fmap
maps ML
-functions, así que
functor StructB (StructA : SigA) :> FUNCTOR =
struct
fmap g = ...
...
end
es un funtor F: StructA -> StructB
.
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)
module type Order = sig type t val compare: t -> t -> bool end;; module Integers = struct type t = int let compare x y = x > y end;; module ReverseOrder = functor (X: Order) -> struct type t = X.t let compare x y = X.compare y x end;; (* We can order reversely *) module K = ReverseOrder (Integers);; Integers.compare 3 4;; (* this is false *) K.compare 3 4;; (* this is true *) module LexicographicOrder = functor (X: Order) -> functor (Y: Order) -> struct type t = X.t * Y.t let compare (a,b) (c,d) = if X.compare a c then true else if X.compare c a then false else Y.compare b d end;; (* compare lexicographically *) module X = LexicographicOrder (Integers) (Integers);; X.compare (2,3) (4,5);; module LinearSearch = functor (X: Order) -> struct type t = X.t array let find x k = 0 (* some boring code *) end;; module BinarySearch = functor (X: Order) -> struct type t = X.t array let find x k = 0 (* some boring code *) end;; (* linear search over arrays of integers *) module LS = LinearSearch (Integers);; LS.find [|1;2;3] 2;; (* binary search over arrays of pairs of integers, sorted lexicographically *) module BS = BinarySearch (LexicographicOrder (Integers) (Integers));; BS.find [|(2,3);(4,5)|] (2,3);;
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 tipo (tipos parametrizados como listas, conjuntos) que pueden ser "mapeados". Para ser precisos, un functor
f
está equipado con(a -> b) -> (fa -> fb)
. Esto tiene sus orígenes en la teoría de categorías. El artículo de Wikipedia al que has vinculado es este uso.class Functor f where fmap :: (a -> b) -> (f a -> f b) instance Functor [] where -- lists are a functor fmap = map instance Functor Maybe where -- Maybe is option in Haskell fmap f (Just x) = Just (f x) fmap f Nothing = Nothing fmap (+1) [2,3,4] -- this is [3,4,5] fmap (+1) (Just 5) -- this is Just 6 fmap (+1) Nothing -- this is Nothing
Entonces, este es un tipo especial de constructores de tipo, ¡y tiene poco que ver con los funtores en Ocaml!
- En lenguajes imperativos, es un puntero para funcionar.
Hay un ejemplo bastante bueno en el libro O''Reilly OCaml que se encuentra en el sitio web de Inria (que al momento de escribirlo, desafortunadamente está abajo). Encontré un ejemplo muy similar en este libro usado por caltech: Introducción a OCaml (enlace pdf) . La sección relevante es el capítulo sobre los funtores (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á en / no en el conjunto ha sido parametrizada (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 que no distingue 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:
module SSet = MakeSet(StringCaseEqual);;
que crea un módulo para una estructura de datos establecida que usa comparaciones que no distinguen entre mayúsculas y minúsculas. Si quisiera crear un conjunto que utilizara comparaciones entre mayúsculas y minúsculas, solo tendría que implementar un nuevo módulo de comparación en lugar de un nuevo módulo de estructura de datos.
Tobu comparó los funtores con las plantillas en C ++, lo cual creo que es bastante adecuado.
La mejor respuesta a esa pregunta se encuentra en "Typeclassopedia" por Brent Yorgey.
Este número de Monad Reader contiene una definición precisa de lo que es un funtor, así como la definición de otros conceptos y un diagrama. (Monoides, aplicativos, mónadas y otros conceptos se explican y se ven en relación con un funtor).
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 de manera uniforme a cada elemento del contenedor"
Pero realmente toda la tipografía es una lectura altamente recomendada que es sorprendentemente fácil. En cierto modo, puede ver la clase de tipos que se presenta allí como un patrón paralelo de diseño en un objeto, en el sentido de que le proporcionan un vocabulario para un comportamiento o capacidad dados.
Aclamaciones
La palabra "funtor" 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 característica 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 equilibrada de una vez por todas como un funtor, 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 orden 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 otros valores que no sean para copiarlos, mientras que el árbol definitivamente necesita poder comparar claves, y obtiene la función de comparación de el parametro del functor)
Otra aplicación de los funtores de ML son los protocolos de red en capas . El enlace es a un papel realmente genial por el grupo CMU Fox; muestra cómo utilizar los funtores para crear capas de protocolo más complejas (como TCP) en el tipo 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 que solo existen en la mente del programador. En 1994, cuando se publicó este trabajo, fue un gran problema.
Para ver un ejemplo desenfrenado de los funtores de ML en acción, puede ver el artículo ML Module Mania , que contiene un ejemplo publicable (es decir, aterrador) de funtores en acción. Para una explicación brillante, clara y clara del sistema de módulos ML (con comparaciones con otros tipos de módulos), lea las primeras páginas del brillante papel POPL de 1994 de Xavier Leroy Tipos de manifiesto, módulos y compilación separada .
En Haskell, y en algún lenguaje funcional puro relacionado,
Functor
es una clase de tipos . 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
está parametrizado sobre otro tipo, que debe considerarse como el tipo de elemento de la colección. El tipo de la colección completa es entonces algo comoT Int
,T String
,T Bool
, si está conteniendo enteros, strings o Booleans 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
), el tipoMaybe
(cero o un elemento 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 que puedas imaginar.La otra propiedad que
T
tiene que satisfacer es que si tiene una función de tipoa -> b
(una función en elementos), entonces tiene que poder tomar esa función y producir una función relacionada en las colecciones. Esto se hace con el operadorfmap
, que es compartido por cada tipo en la clase de tipoFunctor
. El operador está realmente sobrecargado, por lo que si tiene una funcióneven
con el tipoInt -> Bool
, entoncesfmap even
Es una función sobrecargada que puede hacer muchas cosas maravillosas:
Convertir una lista de enteros a una lista de booleanos
Convertir un árbol de enteros en un árbol de booleanos
Nothing
conviertaNothing
aNothing
yJust 7
aJust False
En Haskell, esta propiedad se expresa dando el tipo de
fmap
:fmap :: (Functor t) => (a -> b) -> t a -> t b
donde ahora tenemos una pequeña
t
, que significa "cualquier tipo en la claseFunctor
".
Para
fmap
, en Haskell, un functor es un tipo de colección para la cual, si se le asigna una función en elementos,fmap
le devolverá una función en colecciones . Como puedes imaginar, esta es una idea que puede ser ampliamente reutilizada, por lo que ha sido bendecida como parte de la biblioteca estándar de Haskell.
Como de costumbre, las personas continúan inventando abstracciones nuevas y útiles, y es posible que desee consultar los funtores aplicativos , para los cuales la mejor referencia puede ser un artículo llamado Programación aplicativa con efectos de Conor McBride y Ross Paterson.
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:
[1, 2, 5, 10].map(function(x) { return x*x; });
Salida: [1, 4, 25, 100]
El método de mapa 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:
public interface IntMapFunction {
public int f(int x);
}
Luego, si agrega una clase de colección que tenía una función de mapa, podría hacer:
myCollection.map(new IntMapFunction() { public int f(int x) { return x * x; } });
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 idiomas OO también tienen soporte para funciones directamente, por lo que no es necesario.
Referencia: http://en.wikipedia.org/wiki/Function_object
Otras respuestas aquí están completas, pero intentaré otra explicación del uso de FPtor del functor . Toma esto como una analogía:
Un functor es un contenedor de tipo a que, cuando se somete a una función que se mapea de a → b , produce un contenedor de tipo b .
A diferencia del uso del puntero de función abstracta en C ++, aquí el functor no es la función; más bien, es algo que se comporta constantemente cuando está sujeto a una función .
Tienes bastantes buenas respuestas. Voy a colaborar
Un funtor, en el sentido matemático, es un tipo especial de función en un álgebra. Es una función mínima que mapea un álgebra a otro álgebra. "Minimalidad" es expresada por las leyes del funtor.
Hay dos maneras de ver esto. Por ejemplo, las listas son funtores sobre algún tipo. Es decir, dado un álgebra sobre un tipo ''a'', puede generar un álgebra compatible de listas que contengan cosas de tipo ''a''. (Por ejemplo: el mapa que lleva un elemento a una lista de singleton que lo contiene: f (a) = [a]) De nuevo, la noción de compatibilidad se expresa en las leyes de functor.
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 mapea fa a f b. En resumen, fmap es la parte de F que mapea "partes de funtor" a "partes de funtor", yg es la parte de la función que mapea "partes de álgebra" a "partes de álgebra". Toma una función, un functor, y una vez completa, ES un functor también.
Puede parecer que diferentes idiomas utilizan diferentes nociones de functores, pero no lo son. Simplemente están usando funtores sobre diferentes álgebras. OCamls tiene un álgebra de módulos, y los funtores sobre ese álgebra le permiten adjuntar nuevas declaraciones a un módulo de una manera "compatible".
Un functor de Haskell NO es una clase de tipos. 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 constructor de valores, es una función que mapea Int a F Int, un álgebra equivalente. Es un funtor. Por otra parte, usted no recibe fmap de forma gratuita aquí. Para eso es el patrón de coincidencia.
Los funcionales son buenos para "unir" cosas a elementos de álgebras, de una manera algebraicamente compatible.
En pocas palabras, un functor, u objeto de función, es un objeto de clase que puede llamarse como una función.
En C ++:
Así es como se escribe una función.
void foo()
{
cout << "Hello, world! I''m a function!";
}
Así es como se escribe un funtor.
class FunctorClass
{
public:
void operator ()
{
cout << "Hello, world! I''m a functor!";
}
};
Ahora puedes hacer esto:
foo(); //result: Hello, World! I''m a function!
FunctorClass bar;
bar(); //result: Hello, World! I''m a functor!
Lo que hace que estos sean tan geniales es que puedes mantenerte en la clase; imagínate si quisieras preguntar a una función cuántas veces se ha llamado. No hay manera de hacer esto de forma limpia y encapsulada. Con un objeto de función, es como cualquier otra clase: tendrías una variable de instancia en la que incrementas operator ()
y algún método para inspeccionar esa variable, y todo está ordenado como quieras.
En la práctica, functor significa un objeto que implementa el operador de llamada en C ++. En ocaml, creo que funtor se refiere a algo que toma un módulo como entrada y salida a otro módulo.
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 puede llamarse como sería una función.