web-dev-qa-db-fra.com

En programmation fonctionnelle, qu'est-ce qu'un foncteur?

J'ai rencontré le terme 'Functor' à quelques reprises en lisant divers articles sur la programmation fonctionnelle, mais les auteurs supposent généralement que le lecteur comprend déjà le terme. En parcourant le Web, on a fourni soit des descriptions excessivement techniques (voir le article Wikipedia ), soit des descriptions incroyablement vagues (voir la section sur les foncteurs à ce site Web de tutoriel ).

Quelqu'un peut-il bien vouloir définir le terme, expliquer son utilisation et peut-être donner un exemple de la création et de l'utilisation de functors?

Edit : Bien que la théorie derrière le terme m’intéresse, je m’intéresse moins à la théorie qu’à la mise en oeuvre et à l’utilisation pratique du concept .

Edit 2 : On dirait qu'il y a une terminaison croisée qui se passe: je fais spécifiquement référence aux foncteurs de la programmation fonctionnelle, pas aux objets de fonction de C++ .

218
Erik Forbes

Le mot "foncteur" vient de la théorie des catégories, qui est une branche très générale, très abstraite des mathématiques. Il a été emprunté par les concepteurs de langages fonctionnels d’au moins deux manières différentes.

  • Dans la famille de langues ML, un foncteur est un module qui prend un ou plusieurs autres modules en paramètre. C'est considéré comme une fonctionnalité avancée et la plupart des programmeurs débutants ont des difficultés avec cela.

    À titre d’exemple d’implémentation et d’utilisation pratique, vous pouvez définir votre forme préférée d’arbre de recherche binaire équilibré une fois pour toutes en tant que foncteur. Ce module prendrait en paramètre un module fournissant:

    • Le type de clé à utiliser dans l'arborescence binaire

    • Une fonction de commande totale sur les touches

    Une fois que vous avez fait cela, vous pouvez utiliser la même implémentation d'arborescence binaire équilibrée pour toujours. (Le type de valeur stocké dans l'arborescence reste généralement polymorphe. L'arborescence n'a pas besoin de regarder les valeurs, mais de la copier, alors qu'elle doit absolument pouvoir comparer les clés et obtient la fonction de comparaison. le paramètre du foncteur.)

    Une autre application des foncteurs ML est protocoles de réseau en couches . Le lien est avec un article vraiment formidable du groupe CMU Fox; il montre comment utiliser des foncteurs pour construire des couches de protocole plus complexes (comme TCP) sur des types de couches plus simples (comme IP ou même directement par Ethernet). Chaque couche est implémentée en tant que foncteur qui prend en paramètre la couche en dessous. La structure du logiciel reflète en fait la façon dont les gens perçoivent le problème, par opposition aux couches existant uniquement dans l’esprit du programmeur. En 1994, lorsque ce travail a été publié, c'était une grosse affaire.

    Pour un exemple sauvage de foncteurs ML en action, vous pouvez voir le document ML Module Mania , qui contient un exemple publiable (c'est-à-dire effrayant) de foncteurs au travail. Pour une explication brillante et claire du système de modules ML (avec des comparaisons avec d’autres types de modules), lisez les premières pages du brillant document POPL 1994 de Xavier Leroy Types de manifestes, modules et compilation séparée .

  • En Haskell et dans certains langages fonctionnels purs apparentés, Functor est un type class. Un type appartient à une classe de type (ou plus techniquement, le type "est une instance de" la classe de type) lorsque le type fournit certaines opérations avec certains comportements attendus. Un type T peut appartenir à la classe Functor s'il présente un comportement semblable à celui d'une collection:

    • Le type T est paramétré sur un autre type, que vous devriez considérer comme le type d'élément de la collection. Le type de la collection complète ressemble alors à quelque chose comme T Int, T String, T Bool, si vous utilisez des nombres entiers, des chaînes ou des booléens, respectivement. Si le type d'élément est inconnu, il est écrit sous la forme paramètre typea, comme dans T a.

      Exemples: listes (zéro ou plusieurs éléments de type a), le type Maybe (zéro ou un élément de type a), des ensembles d’éléments de type a, des tableaux d’éléments de type a, toutes sortes d’arbres de recherche contenant des valeurs de type a, et bien d’autres que vous pouvez imaginer.

    • L’autre propriété que T doit satisfaire est que si vous avez une fonction de type a -> b (une fonction sur les éléments), vous devez alors pouvoir utiliser cette fonction et produire une fonction associée sur les collections. Vous faites cela avec l'opérateur fmap, qui est partagé par tous les types de la classe de types Functor. L’opérateur est en fait surchargé, donc si vous avez une fonction even avec le type Int -> Bool, puis

      fmap even
      

      est une fonction surchargée qui peut faire beaucoup de choses merveilleuses:

      • Convertir une liste d'entiers en une liste de booléens

      • Convertir un arbre d'entiers en un arbre de booléens

      • Convertir Nothing en Nothing et Just 7 à Just False

      En Haskell, cette propriété est exprimée en donnant le type de fmap:

      fmap :: (Functor t) => (a -> b) -> t a -> t b
      

      où nous avons maintenant un petit t, ce qui signifie "tout type dans la classe Functor."

    En bref, dans Haskell , un foncteur est une sorte de collection pour laquelle, si on vous attribue une fonction sur des éléments, fmap vous rendra une fonction sur les collections. . Comme vous pouvez l’imaginer, c’est une idée qui peut être largement réutilisée. C’est pourquoi elle est une bénédiction dans la bibliothèque standard de Haskell.

Comme d’habitude, les gens continuent d’inventer de nouvelles abstractions utiles, et vous voudrez peut-être vous pencher sur les foncteurs applicatifs, pour lesquels la meilleure référence peut être un article intitulé Programmation applicative avec effets Conor McBride et Ross Paterson.

265
Norman Ramsey

Les autres réponses ici sont complètes, mais je vais essayer une autre explication de FP utilisation de foncteur. Prenez ceci comme une analogie:

Un foncteur est un conteneur de type a qui, lorsqu'il est soumis à une fonction qui mappe depuis ab, donne un conteneur de type - b.

Contrairement au pointeur de fonction abstraite utilisé en C++, le foncteur est ici not la fonction; c'est plutôt quelque chose qui se comporte de façon constante quand soumis à une fonction.

60
seh

Il y a trois significations différentes, pas beaucoup liées!

  • En Ocaml, il s'agit d'un module paramétré. Voir manuel . Je pense que la meilleure façon de les maîtriser est de donner l'exemple: (écrit rapidement, peut-être un buggy)

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

Vous pouvez maintenant ajouter rapidement de nombreuses commandes possibles, des façons de former de nouvelles commandes, effectuer facilement une recherche binaire ou linéaire. Programmation générique FTW.

  • Dans les langages de programmation fonctionnels comme Haskell, cela signifie certains constructeurs de types (types paramétrés comme des listes, des ensembles) pouvant être "mappés". Pour être précis, un foncteur f est équipé de (a -> b) -> (f a -> f b). Cela a ses origines dans la théorie des catégories. L'article de Wikipedia auquel vous êtes lié correspond à cet usage.

    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
    

Donc, il s’agit d’un type spécial de constructeur de type, qui n’a que peu à voir avec les foncteurs dans Ocaml!

  • Dans les langues impératives, c'est un pointeur vers la fonction.
37
sdcvvc

En OCaml, c'est un module paramétré.

Si vous connaissez le C++, considérez un foncteur OCaml comme un modèle. C++ n'a que des modèles de classe et les foncteurs fonctionnent à l'échelle du module.

Un exemple de foncteur est Map.Make; module StringMap = Map.Make (String);; construit un module de carte qui fonctionne avec les cartes à clé String.

Vous ne pouvez pas obtenir quelque chose comme StringMap avec juste un polymorphisme; vous devez faire des suppositions sur les clés. Le module String contient les opérations (comparaison, etc.) sur un type de chaîne totalement ordonné, et le foncteur sera lié aux opérations contenues dans le module String. Vous pourriez faire quelque chose de similaire avec la programmation orientée objet, mais vous auriez un temps système indirectionnel.

15
Tobu

Vous avez quelques bonnes réponses. Je vais participer:

Un foncteur, au sens mathématique du terme, est une fonction particulière d'une algèbre. C'est une fonction minimale qui associe une algèbre à une autre algèbre. La "minimalité" est exprimée par les lois du foncteur.

Il y a deux façons de regarder ceci. Par exemple, les listes sont des foncteurs sur certains types. Autrement dit, étant donné une algèbre sur un type "a", vous pouvez générer une algèbre compatible contenant des éléments de type "a". (Par exemple: la carte qui prend un élément dans une liste de singleton qui la contient: f(a) = [a]) Encore une fois, la notion de compatibilité est exprimée par les lois du foncteur.

D'autre part, étant donné qu'un foncteur f "sur" un type a, (c'est-à-dire que fa est le résultat de l'application du foncteur f à l'algèbre de type a) et d'une fonction de g: a -> b, nous pouvons calculer un nouveau foncteur F = (fmap g) qui associe fa à f b. En bref, fmap est la partie de F qui mappe les "parties de foncteur" en "parties de foncteur", et g est la partie de la fonction qui mappe les "parties d'algèbre" en "parties d'algèbre". Il faut une fonction, un foncteur, et une fois terminé, il IS aussi un foncteur.

Il peut sembler que différentes langues utilisent différentes notions de foncteurs, mais ce n'est pas le cas. Ils utilisent simplement des foncteurs sur différentes algèbres. OCamls a une algèbre de modules, et les foncteurs sur cette algèbre vous permettent d'attacher de nouvelles déclarations à un module de manière "compatible".

Un foncteur Haskell n'est PAS une classe de type. C'est un type de données avec une variable libre qui satisfait à la classe de type. Si vous êtes prêt à creuser dans les entrailles d'un type de données (sans variables libres), vous pouvez réinterpréter un type de données en tant que foncteur sur une algèbre sous-jacente. Par exemple:

données F = F Int

est isomorphe à la classe d'Ints. So F, en tant que constructeur de valeurs, est une fonction qui mappe Int sur F Int, une algèbre équivalente. C'est un foncteur. Par contre, vous ne recevez pas de fmap gratuitement ici. C'est à ça que correspond le motif.

Les foncteurs sont bons pour "attacher" des choses à des éléments d'algèbre, d'une manière compatible algébriquement.

13
user276631

La meilleure réponse à cette question se trouve dans "Typeclassopedia" de Brent Yorgey.

Ce numéro de Monad Reader contient une définition précise de ce qu'est un foncteur, ainsi que de nombreuses définitions d'autres concepts, ainsi qu'un diagramme. (Monoid, Applicative, Monad et d'autres concepts sont expliqués et vus en relation avec un foncteur).

http://haskell.org/sitewiki/images/8/85/TMR-Issue13.pdf

extrait de Typeclassopedia for Functor: "Une intuition simple est qu'un Functor représente un" conteneur ", avec la possibilité d'appliquer une fonction de manière uniforme à chaque élément du conteneur"

Mais en réalité, toute la classe est une lecture hautement recommandée qui est étonnamment facile. D'une certaine manière, vous pouvez voir la classe de type présentée ici comme un parallèle avec le motif de conception d'objet, en ce sens qu'elle vous donne un vocabulaire pour un comportement ou une capacité donnée.

À votre santé

7
JFT

Il existe un très bon exemple dans le livre O'Reilly OCaml qui se trouve sur le site Web d'Inria (au moment de la rédaction de cet article, malheureusement, il est en panne). J'ai trouvé un exemple très similaire dans ce livre utilisé par caltech: Introduction à OCaml (lien pdf) . La section correspondante est le chapitre sur les foncteurs (page 139 du livre, page 149 du fichier PDF).

Dans le livre, ils ont un foncteur appelé MakeSet qui crée une structure de données qui consiste en une liste et des fonctions pour ajouter un élément, déterminer si un élément est dans la liste et le rechercher. La fonction de comparaison utilisée pour déterminer si elle est dans ou non dans le jeu a été paramétrée (ce qui fait de MakeSet un foncteur au lieu d'un module).

Ils ont également un module qui implémente la fonction de comparaison afin qu’elle compare les chaînes sans tenir compte de la casse.

En utilisant le foncteur et le module qui implémente la comparaison, ils peuvent créer un nouveau module sur une ligne:

module SSet = MakeSet(StringCaseEqual);;

qui crée un module pour une structure de données d'ensemble utilisant des comparaisons insensibles à la casse. Si vous souhaitez créer un ensemble qui utilise des comparaisons sensibles à la casse, il vous suffira d'implémenter un nouveau module de comparaison au lieu d'un nouveau module de structure de données.

Tobu a comparé les foncteurs aux templates en C++, ce qui me semble tout à fait approprié.

7
Niki Yoshiuchi

Voici un article sur les foncteurs d'un POV de programmation , suivi de manière plus spécifique comment ils apparaissent dans les langages de programmation .

L'utilisation pratique d'un foncteur est dans une monade, et vous pouvez trouver de nombreux tutoriels sur les monades si vous le cherchez.

5
Craig Stuntz

Compte tenu des autres réponses et de ce que je vais publier maintenant, je dirais que c'est un mot plutôt surchargé, mais quoi qu'il en soit ...

Pour un indice concernant la signification du mot 'functor' en Haskell, demandez à 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

Donc, fondamentalement, un foncteur en Haskell est quelque chose qui peut être cartographié. Une autre façon de dire est qu'un foncteur est quelque chose qui peut être considéré comme un conteneur auquel on peut demander d'utiliser une fonction donnée pour transformer la valeur qu'il contient; ainsi, pour les listes, fmap coïncide avec map, pour Maybe, fmap f (Just x) = Just (f x), fmap f Nothing = Nothing etc.

La classe de types de foncteurs et la section sur Functors, foncteurs applicatifs et monoïdes de Apprenez-vous un Haskell pour le plus grand bien donnez quelques exemples de cas où cela le concept est utile. (Un résumé: beaucoup de places! :-))

Notez que n'importe quelle monade peut être traitée comme un foncteur et qu'en fait, comme le souligne Craig Stuntz, les foncteurs les plus utilisés sont généralement des monades ... OTOH, il est parfois pratique de faire d'un type une instance de la classe Functor sans prendre la peine d'en faire une Monade. (Par exemple, dans le cas de ZipList de Control.Applicative, Mentionné sur ne des pages susmentionnées .)

5
Michał Marczyk

Dans un commentaire adressé au plus voté réponse , l'utilisateur Wei H demande:

Je comprends les foncteurs ML et Haskell, mais je manque de perspicacité pour les relier. Quelle est la relation entre ces deux, dans un sens catégorique?

Remarque : Je ne connais pas ML, veuillez donc pardonner et corriger les erreurs s'y rapportant.

Supposons d’abord que nous connaissons tous les définitions de "catégorie" et de "foncteur".

Une réponse compacte serait que les "foncteurs Haskell" sont des (endo-) foncteurs F : Hask -> Hask tandis que les "ML-functors" sont des foncteurs G : ML -> ML'.

Ici, Hask est la catégorie formée par les types Haskell et leurs fonctions, et de la même façon ML et ML' sont des catégories définies par des structures ML.

Note : Il existe quelques problèmes techniques pour faire de Hask une catégorie, mais il existe des moyens de les contourner.

Du point de vue de la théorie des catégories, cela signifie qu'un foncteur Hask- est une carte F de types Haskell:

data F a = ...

avec une carte fmap de fonctions Haskell:

instance Functor F where
    fmap f = ...

ML est à peu près la même chose, même s’il n’ya pas d’abstraction fmap canonique que je connaisse, alors définissons-en une:

signature FUNCTOR = sig
  type 'a f
  val fmap: 'a -> 'b -> 'a f -> 'b f
end

C’est-à-dire f maps ML- types et fmap maps ML- fonctions, donc

functor StructB (StructA : SigA) :> FUNCTOR =
struct
  fmap g = ...
  ...
end

est un foncteur F: StructA -> StructB.

5
Ncat

"Functor est un mapping d'objets et de morphismes qui préserve la composition et l'identité d'une catégorie."

Permet de définir ce qu'est une catégorie

C'est un tas d'objets!

Dessinez quelques points (pour l’instant 2 points, l’un est "a", l’autre est "b") dans un cercle et nommez-le A(Category) pour le moment.

Que contient la catégorie?

Composition entre objets et fonction Identité pour chaque objet.

Nous devons donc cartographier les objets et préserver la composition après l’application de notre Functor.

Imaginons 'A' est notre catégorie qui a des objets ['a', 'b'] et il existe un morphisme a -> b

Maintenant, nous devons définir un foncteur qui mappera ces objets et ces morphismes dans une autre catégorie 'B'.

Disons que le foncteur s'appelle 'Peut-être'

data Maybe a = Nothing | Just a

Donc, la catégorie 'B' ressemble à ceci.

Veuillez dessiner un autre cercle, mais cette fois-ci avec "Peut-être a" et "Peut-être b" au lieu de "a" et "b".

Tout semble bon et tous les objets sont cartographiés

'a' est devenu 'Maybe a' et 'b' est devenu 'Maybe b'.

Mais le problème est que nous devons également cartographier le morphisme de 'a' à 'b'.

Cela signifie que le morphisme a -> b dans 'A' devrait correspondre au morphisme 'Peut-être a' -> 'Peut-être b'

morphisme de a -> b est appelé f, puis morphisme de 'Peut-être a' -> 'Peut-être b' est appelé 'fmap f'

Voyons maintenant quelle fonction "f" faisait dans "A" et voyons si nous pouvons la reproduire dans "B"

définition de la fonction 'f' dans 'A':

f :: a -> b

f prend a et retourne b

définition de la fonction 'f' dans 'B':

f :: Maybe a -> Maybe b

f prend Peut-être a et retour Peut-être b

voyons comment utiliser fmap pour mapper la fonction 'f' de 'A' à la fonction 'fmap f' dans 'B'

définition de fmap

fmap :: (a -> b) -> (Maybe a -> Maybe b)
fmap f Nothing = Nothing
fmap f (Just x) = Just(f x)

Alors, qu'est-ce qu'on fait ici?

Nous appliquons la fonction 'f' à 'x' qui est de type 'a'. La correspondance de modèle spéciale de 'Nothing' provient de la définition de Functor Maybe.

Nous avons donc cartographié nos objets [a, b] et nos morphismes [f] de la catégorie "A" à la catégorie "B".

Thats Functor!

enter image description here

4
Sumanth Kumar Mora

Vue d'ensemble

En programmation fonctionnelle, un foncteur est essentiellement une construction consistant à lever des fonctions ordinaires unaires (c'est-à-dire avec un argument) pour fonctions entre variables de nouveaux types. Il est beaucoup plus facile d'écrire et de gérer des fonctions simples entre des objets simples et d'utiliser des foncteurs pour les soulever, puis pour écrire manuellement des fonctions entre des objets conteneurs complexes. Un autre avantage est d’écrire une seule fois les fonctions simples, puis de les réutiliser via différents foncteurs.

Les exemples de foncteurs incluent les tableaux, les foncteurs "peut-être" et "soit", les futures (voir par exemple https://github.com/Avaq/Fluture ), et beaucoup d'autres.

Illustration

Considérons la fonction qui construit le nom complet de la personne à partir du prénom et du nom. Nous pourrions le définir comme fullName(firstName, lastName) comme fonction de deux arguments, ce qui ne conviendrait cependant pas pour les foncteurs qui ne traitent que les fonctions d'un argument. Pour remédier à cela, nous recueillons tous les arguments dans un seul objet name, qui devient désormais l'argument unique de la fonction:

// In JavaScript notation
fullName = name => name.firstName + ' ' + name.lastName

Maintenant, si nous avons beaucoup de gens dans un tableau? Au lieu de parcourir la liste manuellement, nous pouvons simplement réutiliser notre fonction fullName via la méthode map fournie pour les tableaux avec une seule ligne de code:

fullNameList = nameList => nameList.map(fullName)

et l'utiliser comme

nameList = [
    {firstName: 'Steve', lastName: 'Jobs'},
    {firstName: 'Bill', lastName: 'Gates'}
]

fullNames = fullNameList(nameList) 
// => ['Steve Jobs', 'Bill Gates']

Cela fonctionnera chaque fois que chaque entrée de notre nameList sera un objet fournissant les propriétés firstName et lastName. Mais que se passe-t-il si certains objets ne le sont pas (ou même ne le sont pas du tout)? Pour éviter les erreurs et rendre le code plus sûr, nous pouvons envelopper nos objets dans le type Maybe (voir par exemple 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()

Just(name) est un conteneur ne contenant que des noms valides et Nothing() est la valeur spéciale utilisée pour tout le reste. Maintenant, au lieu d'interrompre (ou d'oublier) de vérifier la validité de nos arguments, nous pouvons simplement réutiliser (lever) notre fonction originale fullName avec une autre ligne de code, basée à nouveau sur la méthode map , cette fois prévu pour le type Maybe:

// Maybe Object -> Maybe String
maybeFullName = maybeName => maybeName.map(fullName)

et l'utiliser comme

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()

Théorie des catégories

Un foncteur dans Théorie des catégories est une carte entre deux catégories respectant la composition de leurs morphismes. Dans un langage informatique, la principale catégorie d'intérêt est celle dont les objets sont types (certains ensembles de valeurs), et dont les morphismes sont des fonctions f:a->b d'un type a à un autre tapez b.

Par exemple, prenons a comme étant le type String, b le type Number et f est la fonction mappant une chaîne dans sa longueur:

// f :: String -> Number
f = str => str.length

Ici a = String Représente l'ensemble de toutes les chaînes et b = Number L'ensemble de tous les nombres. En ce sens, a et b représentent tous deux des objets dans Set Category (qui est étroitement liée à la catégorie des types, la différence étant ici non essentielle. ). Dans la catégorie des ensembles, les morphismes entre deux ensembles correspondent précisément à toutes les fonctions du premier ensemble au second. Donc, notre fonction de longueur f est un morphisme de l'ensemble des chaînes en l'ensemble des nombres.

Comme nous ne considérons que la catégorie définie, les Functors pertinents sont des cartes envoyant des objets à des objets et des morphismes à des morphismes, qui satisfont à certaines lois algébriques.

Exemple: Array

Array peut signifier beaucoup de choses, mais un seul est un Functor - la construction de type mappant un type a dans le type [a] de tous les tableaux de type a. Par exemple, le foncteur Array mappe le type String dans le type [String] (L'ensemble de tous les tableaux de chaînes de longueur arbitraire) et le type Number dans le type correspondant [Number] (l'ensemble de tous les tableaux de nombres).

Il est important de ne pas confondre la carte du foncteur

Array :: a => [a]

avec un morphisme a -> [a]. Le foncteur mappe simplement (associe) le type a au type [a] Comme une chose à une autre. Que chaque type soit en réalité un ensemble d’éléments, n’a aucune pertinence ici. En revanche, un morphisme est une fonction réelle entre ces ensembles. Par exemple, il y a un morphisme naturel (fonction)

pure :: a -> [a]
pure = x => [x]

qui envoie une valeur dans le tableau à 1 élément avec cette valeur comme entrée unique. Cette fonction n'est pas une partie du foncteur Array! Du point de vue de ce foncteur, pure n’est qu’une fonction comme une autre, rien de spécial.

D'autre part, le foncteur Array a sa deuxième partie - la partie morphisme. Ce qui transforme un morphisme f :: a -> b En un morphisme [f] :: [a] -> [b]:

// a -> [a]
Array.map(f) = arr => arr.map(f)

Ici arr est un tableau de longueur arbitraire avec des valeurs de type a, et arr.map(f) est un tableau de même longueur avec des valeurs de type b, dont les entrées sont le résultat de l'application de f aux entrées de arr. Pour en faire un foncteur, les lois mathématiques du mappage identité à identité et des compositions à compositions doivent être vérifiées, faciles à vérifier dans cet exemple Array.

2
Dmitri Zaitsev

Ne pas contredire les réponses théoriques ou mathématiques précédentes, mais un foncteur est également un objet (dans un langage de programmation orienté objet) qui n’a qu’une méthode et qui est effectivement utilisé comme fonction.

Un exemple est l'interface Runnable en Java, qui n'a qu'une seule méthode: run.

Considérons cet exemple, d'abord en Javascript, qui a des fonctions de première classe:

[1, 2, 5, 10].map(function(x) { return x*x; });

Sortie: [1, 4, 25, 100]

La méthode map prend une fonction et renvoie un nouveau tableau, chaque élément étant le résultat de l'application de cette fonction à la valeur située à la même position dans le tableau d'origine.

Pour faire la même chose en Java, en utilisant un Functor, vous devez d’abord définir une interface, par exemple:

public interface IntMapFunction {
  public int f(int x);
}

Ensuite, si vous ajoutez une classe de collection qui avait une fonction map, vous pourriez faire:

myCollection.map(new IntMapFunction() { public int f(int x) { return x * x; } });

Ceci utilise une sous-classe en ligne de IntMapFunction pour créer un Functor, qui est l'équivalent OO de la fonction de l'exemple JavaScript précédent.

Utiliser Functors vous permet d’appliquer des techniques fonctionnelles dans une langue OO. Bien sûr, certaines langues OO supportent également les fonctions directement, ce qui n’est pas nécessaire.

Référence: http://en.wikipedia.org/wiki/Function_object

2
Kevin Greer

KISS: Un foncteur est un objet qui a une méthode de carte.

Les tableaux en JavaScript implémentent la carte et sont donc des foncteurs. Les promesses, les flux et les arbres implémentent souvent les cartes dans des langages fonctionnels et, lorsqu'ils le font, ils sont considérés comme des foncteurs. La méthode map du foncteur prend son propre contenu et transforme chacune d’elles en utilisant le rappel de transformation transmis à map, et renvoie un nouveau foncteur contenant la structure en tant que premier foncteur, mais avec les valeurs transformées.

src: https://www.youtube.com/watch?v=DisD9ftUyCk&feature=youtu.be&t=76

0
soundyogi