Encontrei o termo 'Functor' algumas vezes enquanto lia vários artigos sobre programação funcional, mas os autores normalmente assumem que o leitor já entende o termo. A pesquisa na web forneceu descrições excessivamente técnicas (consulte o artigo da Wikipedia ) ou descrições incrivelmente vagas (consulte a seção sobre Functors neste site do ocaml-tutorial ).
Alguém pode definir gentilmente o termo, explicar seu uso e talvez fornecer um exemplo de como os Functors são criados e usados?
Edit : Enquanto estou interessado na teoria por trás do termo, estou menos interessado na teoria do que na implementação e uso prático do conceito.
Edit 2 : Parece que existe alguma terminologia cruzada: estou me referindo especificamente aos Functors da programação funcional, não aos objetos de função do C ++.
fonte
fmap
mapeia as funções. Existem dois tipos de mapeamento envolvidos. Essa maneira de ver as coisas ajudará a entender a teoria das categorias (que é mais geral). Quero dizer, é interessante entender a teoria básica das categorias para nos ajudar com todo o material da teoria das categorias em Haskell (functor, mônadas, ...).Respostas:
A palavra "functor" vem da teoria das categorias, que é um ramo muito geral e muito abstrato da matemática. Foi emprestado por designers de linguagens funcionais de pelo menos duas maneiras diferentes.
Na família de idiomas ML, um functor é um módulo que usa um ou mais outros módulos como parâmetro. É considerado um recurso avançado e a maioria dos programadores iniciantes tem dificuldade com isso.
Como um exemplo de implementação e uso prático, você pode definir sua forma favorita de árvore de pesquisa binária balanceada de uma vez por todas como um functor e tomaria como parâmetro um módulo que fornece:
O tipo de chave a ser usada na árvore binária
Uma função de pedido total nas teclas
Depois de fazer isso, você pode usar a mesma implementação de árvore binária equilibrada para sempre. (O tipo de valor armazenado na árvore geralmente é deixado polimórfico - a árvore não precisa olhar outros valores além de copiá-los, enquanto a árvore definitivamente precisa ser capaz de comparar chaves e obtém a função de comparação de o parâmetro do functor.)
Outra aplicação dos functores de ML é protocolo de rede em camadas . O link é para um artigo realmente fantástico do grupo CMU Fox; mostra como usar functors para criar camadas de protocolo mais complexas (como TCP) em tipos de camadas mais simples (como IP ou mesmo diretamente via Ethernet). Cada camada é implementada como um functor que assume como parâmetro a camada abaixo dela. A estrutura do software realmente reflete a maneira como as pessoas pensam sobre o problema, em oposição às camadas existentes apenas na mente do programador. Em 1994, quando este trabalho foi publicado, foi um grande negócio.
Para um exemplo selvagem de functores de ML em ação, você pode ver o artigo ML Module Mania , que contém um exemplo publicável (isto é, assustador) de functores em ação. Para obter uma explicação brilhante, clara e plúcida do sistema de módulos ML (com comparações com outros tipos de módulos), leia as primeiras páginas do brilhante artigo POPL de 1994 de Xavier Leroy, Manifest Types, Modules e Compilation Separated .
Em Haskell, e em alguma linguagem funcional pura relacionada,
Functor
há uma classe de tipo . Um tipo pertence a uma classe de tipo (ou mais tecnicamente, o tipo "é uma instância" da classe de tipo) quando o tipo fornece determinadas operações com determinado comportamento esperado. Um tipoT
pode pertencer à classeFunctor
se tiver determinado comportamento de coleção:O tipo
T
é parametrizado em relação a outro tipo, que você deve considerar como o tipo de elemento da coleção. O tipo de coleção completa é, então, algo comoT Int
,T String
,T Bool
, se você estiver contendo inteiros, strings, ou Booleans respectivamente. Se o tipo de elemento for desconhecido, ele será gravado como um parâmetro de tipoa
, como emT a
.Os exemplos incluem listas (zero ou mais elementos do tipo
a
), oMaybe
tipo (zero ou um elemento do tipoa
), conjuntos de elementos do tipoa
, matrizes de elementos do tipoa
, todos os tipos de árvores de pesquisa que contêm valores do tipoa
e muitas outras que você pode pensar.A outra propriedade que
T
deve ser satisfeita é que, se você tem uma função do tipoa -> b
(uma função nos elementos), deve poder assumir essa função e produzir uma função relacionada nas coleções. Você faz isso com o operadorfmap
, que é compartilhado por todos os tipos naFunctor
classe type. O operador está realmente sobrecarregado; portanto, se você tiver uma funçãoeven
com o tipoInt -> Bool
,é uma função sobrecarregada que pode fazer muitas coisas maravilhosas:
Converter uma lista de números inteiros em uma lista de booleanos
Converter uma árvore de números inteiros em uma árvore de booleanos
Converter
Nothing
emNothing
eJust 7
paraJust False
Em Haskell, essa propriedade é expressa fornecendo o tipo de
fmap
:onde agora temos um pequeno
t
, o que significa "qualquer tipo naFunctor
classe".Para resumir uma longa história, em Haskell, um functor é um tipo de coleção para a qual, se você receber uma função em elementos,
fmap
retornará uma função em coleções . Como você pode imaginar, essa é uma ideia que pode ser amplamente reutilizada, e é por isso que é abençoada como parte da biblioteca padrão de Haskell.Como sempre, as pessoas continuam a inventar abstrações úteis e úteis, e talvez você queira investigar functores aplicativos , para os quais a melhor referência pode ser um artigo chamado Programação Aplicativa com Efeitos de Conor McBride e Ross Paterson.
fonte
then you have to be able to take that function and product a related function on collections
Você quis dizer emproduce
vez deproduct
?Outras respostas aqui estão completas, mas tentarei outra explicação sobre o uso do functor por FP . Tome isso como analogia:
Diferente do uso do ponteiro de função abstraída em C ++, aqui o functor não é a função; pelo contrário, é algo que se comporta consistentemente quando sujeito a uma função .
fonte
Existem três significados diferentes, não muito relacionados!
No Ocaml, é um módulo parametrizado. Veja o manual . Eu acho que a melhor maneira de grok-los é pelo exemplo: (escrito rapidamente, pode ser buggy)
Agora você pode adicionar rapidamente muitos pedidos possíveis, formas de formar novos pedidos, fazer uma pesquisa binária ou linear facilmente sobre eles. Programação genérica FTW.
Em linguagens de programação funcional como Haskell, significa alguns construtores de tipos (tipos parametrizados como listas, conjuntos) que podem ser "mapeados". Para ser mais preciso, um functor
f
está equipado com(a -> b) -> (f a -> f b)
. Isso tem origens na teoria das categorias. O artigo da Wikipedia ao qual você vinculou é esse uso.Portanto, esse é um tipo especial de construtor de tipos e tem pouco a ver com functors no Ocaml!
fonte
No OCaml, é um módulo parametrizado.
Se você conhece C ++, pense em um functor OCaml como um modelo. O C ++ possui apenas modelos de classe e os functores funcionam na escala do módulo.
Um exemplo de functor é Map.Make;
module StringMap = Map.Make (String);;
cria um módulo de mapa que funciona com mapas com chave de seqüência de caracteres.Você não poderia conseguir algo como o StringMap apenas com polimorfismo; você precisa fazer algumas suposições sobre as chaves. O módulo String contém as operações (comparação, etc) em um tipo de string totalmente ordenado, e o functor será vinculado às operações que o módulo String contém. Você poderia fazer algo semelhante com a programação orientada a objetos, mas teria uma sobrecarga indireta de método.
fonte
Você obteve algumas boas respostas. Vou abordar:
Um functor, no sentido matemático, é um tipo especial de função em uma álgebra. É uma função mínima que mapeia uma álgebra para outra álgebra. "Minimalidade" é expressa pelas leis do functor.
Existem duas maneiras de ver isso. Por exemplo, listas são functors sobre algum tipo. Ou seja, dada uma álgebra sobre um tipo 'a', você pode gerar uma álgebra compatível de listas contendo itens do tipo 'a'. (Por exemplo: o mapa que leva um elemento a uma lista de singleton que o contém: f (a) = [a]) Novamente, a noção de compatibilidade é expressa pelas leis do functor.
Por outro lado, dado um functor f "sobre" um tipo a, (isto é, fa é o resultado da aplicação do functor f à álgebra do tipo a), e função de g: a -> b, podemos calcular um novo functor F = (fmap g) que mapeia fa a f b. Em resumo, fmap é a parte de F que mapeia "partes de functor" para "partes de functor", eg é a parte da função que mapeia "partes de álgebra" para "partes de álgebra". É preciso uma função, um functor e, uma vez concluído, também é um functor.
Pode parecer que idiomas diferentes estão usando noções diferentes de functores, mas não estão. Eles estão apenas usando functores em diferentes álgebras. O OCamls possui uma álgebra de módulos, e os functores dessa álgebra permitem anexar novas declarações a um módulo de maneira "compatível".
Um functor Haskell NÃO é uma classe de tipo. É um tipo de dados com uma variável livre que satisfaz a classe de tipo. Se você estiver disposto a se aprofundar em um tipo de dados (sem variáveis livres), poderá reinterpretar um tipo de dados como um functor em uma álgebra subjacente. Por exemplo:
dados F = F Int
é isomórfico para a classe de Ints. Portanto, F, como construtor de valor, é uma função que mapeia Int para F Int, uma álgebra equivalente. É um functor. Por outro lado, você não recebe o fmap gratuitamente aqui. É para isso que serve a correspondência de padrões.
Functors são bons para "anexar" coisas a elementos de álgebras, de maneira algebricamente compatível.
fonte
A melhor resposta para essa pergunta é encontrada em "Typeclassopedia", de Brent Yorgey.
Esta edição do Monad Reader contém uma definição precisa do que é um functor, além de muitas definições de outros conceitos e um diagrama. (Monóide, Aplicativo, Mônada e outros conceitos são explicados e vistos em relação a um functor).
http://haskell.org/sitewiki/images/8/85/TMR-Issue13.pdf
trecho da Typeclassopedia for Functor: "Uma intuição simples é que um Functor representa um" contêiner "de algum tipo, juntamente com a capacidade de aplicar uma função de maneira uniforme a todos os elementos do contêiner"
Mas, na verdade, toda a tipografia é uma leitura altamente recomendada e surpreendentemente fácil. De certa forma, você pode ver a classe de tipo apresentada lá como um paralelo ao padrão de design no objeto, no sentido em que fornece um vocabulário para determinado comportamento ou capacidade.
Felicidades
fonte
Há um bom exemplo no livro O'Reilly OCaml, que está no site da Inria (que, infelizmente, está escrito abaixo). Encontrei um exemplo muito semelhante neste livro usado pela caltech: Introdução ao OCaml (link em pdf) . A seção relevante é o capítulo sobre functores (página 139 no livro, página 149 no PDF).
No livro, eles têm um functor chamado MakeSet, que cria uma estrutura de dados que consiste em uma lista e funções para adicionar um elemento, determinar se um elemento está na lista e encontrar o elemento. A função de comparação usada para determinar se está no / não no conjunto foi parametrizada (que é o que torna o MakeSet um functor em vez de um módulo).
Eles também têm um módulo que implementa a função de comparação para fazer uma comparação de cadeias sem distinção entre maiúsculas e minúsculas.
Usando o functor e o módulo que implementa a comparação, eles podem criar um novo módulo em uma linha:
que cria um módulo para uma estrutura de dados definida que usa comparações sem distinção entre maiúsculas e minúsculas. Se você quisesse criar um conjunto que usasse comparações com distinção entre maiúsculas e minúsculas, seria necessário implementar um novo módulo de comparação em vez de um novo módulo de estrutura de dados.
Tobu comparou os functores aos modelos em C ++, o que eu acho bastante adequado.
fonte
Dadas as outras respostas e o que vou postar agora, eu diria que é uma palavra bastante sobrecarregada, mas de qualquer maneira ...
Para uma dica sobre o significado da palavra 'functor' em Haskell, pergunte ao GHCi:
Então, basicamente, um functor em Haskell é algo que pode ser mapeado. Outra maneira de dizer isso é que um functor é algo que pode ser considerado como um contêiner que pode ser solicitado a usar uma determinada função para transformar o valor que ele contém; Assim, para as listas,
fmap
coincide commap
, paraMaybe
,fmap f (Just x) = Just (f x)
,fmap f Nothing = Nothing
etc.A subseção de classe de tipo Functor e a seção Functors, Functors Aplicable e Monoids of Learn You a Haskell for Great Good fornecem alguns exemplos de onde esse conceito específico é útil. (Um resumo: muitos lugares! :-))
Observe que qualquer mônada pode ser tratada como um functor e, de fato, como Craig Stuntz salienta, os functores mais usados tendem a ser mônadas ... OTOH, às vezes é conveniente fazer de um tipo uma instância da classe de tipo Functor sem se dar ao trabalho de torná-la uma mônada. (Por exemplo, no caso de
ZipList
fromControl.Applicative
, mencionado em uma das páginas mencionadas acima .)fonte
Aqui está um artigo sobre functores de um POV de programação , seguido mais especificamente de como eles aparecem em linguagens de programação .
O uso prático de um functor está em uma mônada, e você pode encontrar muitos tutoriais sobre mônadas, se procurar por isso.
fonte
Em um comentário à resposta mais votada , o usuário Wei Hu pergunta:
Nota : Como não conheço o ML, perdoe e corrija os erros relacionados.
Vamos inicialmente assumir que todos estamos familiarizados com as definições de 'categoria' e 'functor'.
Uma resposta compacta seria que "Haskell-functors" são (endo-) functores
F : Hask -> Hask
enquanto "ML-functors" são functoresG : ML -> ML'
.Aqui,
Hask
é a categoria formada pelos tipos e funções de Haskell entre eles, e da mesma formaML
eML'
são categorias definidas pelas estruturas de ML.Nota : Existem alguns problemas técnicos na criação de
Hask
uma categoria, mas existem maneiras de contorná-los.De uma perspectiva teórica da categoria, isso significa que um
Hask
-functor é um mapaF
dos tipos Haskell:junto com um mapa
fmap
das funções Haskell:O ML é praticamente o mesmo, embora não exista
fmap
abstração canônica , então vamos definir um:São
f
mapasML
-types efmap
mapsML
-functions, entãoé um functor
F: StructA -> StructB
.fonte
"Functor é o mapeamento de objetos e morfismos que preserva a composição e a identidade de uma categoria".
Vamos definir o que é uma categoria?
O que a categoria contém?
Portanto, temos que mapear os objetos e preservar a composição após aplicar nosso Functor.
Vamos imaginar que 'A' é a nossa categoria que possui objetos ['a', 'b'] e existe um morfismo a -> b
Agora, temos que definir um functor que possa mapear esses objetos e morfismos em outra categoria 'B'.
Vamos dizer que o functor é chamado de 'Talvez'
Portanto, a categoria 'B' se parece com isso.
Desenhe outro círculo, mas desta vez com 'Talvez a' e 'Talvez b' em vez de 'a' e 'b'.
Tudo parece bom e todos os objetos são mapeados
'a' se tornou 'Talvez a' e 'b' se tornou 'Talvez b'.
Mas o problema é que temos que mapear o morfismo de 'a' para 'b' também.
Isso significa que o morfismo a -> b em 'A' deve ser mapeado para o morfismo 'Talvez a' -> 'Talvez b'
o morfismo de a -> b é chamado f, então o morfismo de 'Talvez a' -> 'Talvez b' é chamado de 'fmap f'
Agora vamos ver qual função 'f' estava executando em 'A' e ver se podemos replicá-la em 'B'
definição de função de 'f' em 'A':
f pega ae retorna b
definição de função de 'f' em 'B':
f pega Talvez ae retorna Talvez b
vamos ver como usar o fmap para mapear a função 'f' de 'A' para a função 'fmap f' em 'B'
definição de fmap
Então o que estamos fazendo aqui ?
Estamos aplicando a função 'f' a 'x', que é do tipo 'a'. A correspondência de padrão especial de 'Nothing' vem da definição de
Functor Maybe
.Assim, mapeamos nossos objetos [a, b] e morfismos [f] da categoria 'A' para a categoria 'B'.
Isso é Functor!
fonte
Visão geral aproximada
Na programação funcional, um functor é essencialmente uma construção de elevação de funções unárias comuns (isto é, aquelas com um argumento) para funções entre variáveis de novos tipos. É muito mais fácil escrever e manter funções simples entre objetos simples e usar functors para levantá-los e depois escrever funções manualmente entre objetos contêineres complicados. Outra vantagem é escrever funções simples apenas uma vez e depois reutilizá-las através de diferentes functores.
Exemplos de functores incluem matrizes, "talvez" e "qualquer" functores, futuros (consulte, por exemplo, https://github.com/Avaq/Fluture ) e muitos outros.
Ilustração
Considere a função que constrói o nome completo da pessoa a partir do nome e sobrenome. Poderíamos defini-lo como
fullName(firstName, lastName)
função de dois argumentos, que, no entanto, não seriam adequados para functores que lidam apenas com funções de um argumento. Para remediar, coletamos todos os argumentos em um único objetoname
, que agora se torna o único argumento da função:Agora, e se tivermos muitas pessoas em uma matriz? Em vez de revisar manualmente a lista, podemos simplesmente reutilizar nossa função
fullName
através domap
método fornecido para matrizes com uma única linha de código curta:e use-o como
Isso funcionará, sempre que cada entrada no nosso
nameList
for um objeto que fornece propriedadesfirstName
e tantolastName
. Mas e se alguns objetos não o fizerem (ou nem sequer são objetos)? Para evitar os erros e tornar o código mais seguro, podemos agrupar nossos objetos noMaybe
tipo (por exemplo, https://sanctuary.js.org/#maybe-type ):onde
Just(name)
é um contêiner que contém apenas nomes válidos eNothing()
é o valor especial usado para todo o resto. Agora, em vez de interromper (ou esquecer) para verificar a validade de nossos argumentos, podemos simplesmente reutilizar (elevar) nossafullName
função original com outra linha de código única, baseada novamente nomap
método, desta vez fornecido para o tipo Maybe:e use-o como
Teoria da categoria
Um Functor na Teoria da Categoria é um mapa entre duas categorias, respeitando a composição de seus morfismos. Em uma linguagem de computador , a principal categoria de interesse é aquela cujos objetos são tipos (determinados conjuntos de valores) e cujos morfismos são funções
f:a->b
de um tipoa
para outro tipob
.Por exemplo, considere
a
ser oString
tipo,b
o tipo Number ef
é a função que mapeia uma sequência em seu comprimento:Aqui
a = String
representa o conjunto de todas as strings eb = Number
o conjunto de todos os números. Nesse sentido, ambosa
eb
representam objetos na Categoria de conjunto (que está intimamente relacionada à categoria de tipos, com a diferença aqui não essencial). Na categoria de conjuntos, os morfismos entre dois conjuntos são precisamente todas as funções do primeiro conjunto ao segundo. Portanto, nossa função de comprimentof
aqui é um morfismo do conjunto de cordas para o conjunto de números.Como consideramos apenas a categoria definida, os Functors relevantes a partir dela são mapas que enviam objetos a objetos e morfismos a morfismos, que satisfazem certas leis algébricas.
Exemplo:
Array
Array
pode significar muitas coisas, mas apenas uma coisa é um Functor - a construção de tipo, mapeando um tipoa
no tipo[a]
de todas as matrizes do tipoa
. Por exemplo, oArray
functor mapeia o tipoString
para o tipo[String]
(o conjunto de todas as matrizes de cadeias de comprimento arbitrário) e define o tipoNumber
para o tipo correspondente[Number]
(o conjunto de todas as matrizes de números).É importante não confundir o mapa do Functor
com um morfismo
a -> [a]
. O functor simplesmente mapeia (associa) o tipoa
no tipo[a]
como uma coisa para outra. Que cada tipo é realmente um conjunto de elementos, não tem relevância aqui. Por outro lado, um morfismo é uma função real entre esses conjuntos. Por exemplo, existe um morfismo natural (função)que envia um valor para a matriz de 1 elemento com esse valor como entrada única. Essa função não faz parte do
Array
Functor! Do ponto de vista desse functor,pure
é apenas uma função como outra qualquer, nada de especial.Por outro lado, o
Array
Functor tem sua segunda parte - a parte do morfismo. Que mapeia um morfismof :: a -> b
em um morfismo[f] :: [a] -> [b]
:Aqui
arr
está qualquer matriz de comprimento arbitrário com valores do tipoa
earr.map(f)
é a matriz do mesmo comprimento com valores do tipob
, cujas entradas são resultados da aplicaçãof
às entradas dearr
. Para torná-lo um functor, as leis matemáticas do mapeamento de identidade para identidade e de composições para composições devem ser válidas, que são fáceis de verificar nesteArray
exemplo.fonte
Não para contradizer as respostas teóricas ou matemáticas anteriores, mas um Functor também é um Objeto (em uma linguagem de programação Orientada a Objetos) que possui apenas um método e é efetivamente usado como uma função.
Um exemplo é a interface Runnable em Java, que possui apenas um método: run.
Considere este exemplo, primeiro em Javascript, que possui funções de primeira classe:
Saída: [1, 4, 25, 100]
O método map pega uma função e retorna uma nova matriz, com cada elemento sendo o resultado da aplicação dessa função ao valor na mesma posição na matriz original.
Para fazer a mesma coisa que o Java, usando um Functor, você primeiro precisa definir uma interface, digamos:
Em seguida, se você adicionar uma classe de coleção que possui uma função de mapa, poderá:
Isso usa uma subclasse in-line de IntMapFunction para criar um Functor, que é o equivalente de OO da função do exemplo anterior de JavaScript.
O uso de Functors permite aplicar técnicas funcionais em uma linguagem OO. Obviamente, algumas linguagens OO também têm suporte para funções diretamente, portanto, isso não é necessário.
Referência: http://en.wikipedia.org/wiki/Function_object
fonte
Array
é um functor, masArray(value)
fornece apenas matrizes de 1 elemento.BEIJO: Um functor é um objeto que possui um método de mapa.
As matrizes no JavaScript implementam o mapa e, portanto, são functores. Promessas, fluxos e árvores geralmente implementam o mapa em linguagens funcionais e, quando o fazem, são considerados functores. O método map do functor pega seu próprio conteúdo e transforma cada um deles usando o retorno de chamada de transformação passado para mapear e retorna um novo functor, que contém a estrutura como o primeiro functor, mas com os valores transformados.
src: https://www.youtube.com/watch?v=DisD9ftUyCk&feature=youtu.be&t=76
fonte
Array
A construção de tipo define um único functor. Suas instâncias também são chamadas de "matrizes", mas não são functores. A descrição aqui deve ser mais precisa.Na prática, functor significa um objeto que implementa o operador de chamada em C ++. No ocaml, acho que functor se refere a algo que recebe um módulo como entrada e gera outro módulo.
fonte
Simplificando, um functor, ou objeto de função, é um objeto de classe que pode ser chamado como uma função.
Em C ++:
É assim que você escreve uma função
É assim que você escreve um functor
Agora você pode fazer isso:
O que os torna tão bons é que você pode manter o estado na classe - imagine se você quiser perguntar a uma função quantas vezes ela foi chamada. Não há como fazer isso de maneira organizada e encapsulada. Com um objeto de função, é como qualquer outra classe: você teria alguma variável de instância na qual incrementa
operator ()
e algum método para inspecionar essa variável, e tudo ficará bem como desejar.fonte
FunctorClass
cumpre a primeira Lei de Functor, mas você poderia esboçar uma prova para a segunda Lei? Eu não vejo isso direito.O Functor não está especificamente relacionado à programação funcional. É apenas um "ponteiro" para uma função ou algum tipo de objeto, que pode ser chamado como seria uma função.
fonte