42 votes

Programmation orientée objet dans un contexte de programmation purement fonctionnel?

Quels sont les avantages à l'utilisation de la programmation orientée objet (POO) en programmation fonctionnelle (FP) contexte?

J'ai été en utilisant F# pour un certain temps maintenant, et j'ai remarqué que le plus de mes fonctions sont apatrides, moins j'ai besoin d'avoir comme des méthodes d'objets. En particulier, il y a des avantages à s'appuyer sur l'inférence de type pour les avoir utilisable en tant que large, un certain nombre de situations que possible.

Cela n'empêche pas la nécessité pour les espaces de noms de certaine forme, ce qui est orthogonale à la programmation orientée objet. Ni est l'utilisation de structures de données découragé. En fait, la véritable utilisation de la PF langues dépendent fortement de la structure des données. Si vous regardez le F# pile mises en œuvre en fa Dièse Programmation Avancée Structures de Données, vous trouverez qu'il n'est pas orienté objet.

Dans mon esprit, la programmation orientée objet est fortement associée avec le fait d'avoir des méthodes qui agissent sur l'état de l'objet pour la plupart de muter l'objet. Dans un pur FP contexte qui n'est pas nécessaire ou souhaitée.

Une raison pratique peut être à être en mesure d'interagir avec OOP code, de la même façon F# fonctionne avec .NET. Autre que cela, cependant, existe-il des raisons? Et qu'est-ce que l'expérience dans le Haskell monde, où la programmation est plus pur FP?

Je vais apprécier toutes les références à des documents ou contrefactuelle des exemples du monde réel sur la question.

54voto

C. A. McCann Points 56834

Le déconnectez vous voyez n'est pas de FP vs la programmation orientée objet. C'est surtout à propos de l'immuabilité et de formalismes mathématiques vs de la mutabilité et de façon informelle.

Tout d'abord, nous allons passer de la mutabilité problème: vous pouvez avoir de la PF avec de la mutabilité et de la programmation orientée objet avec l'immutabilité l'amende juste. Encore plus fonctionnel que tu Haskell permet de jouer avec les données mutable tout ce que vous voulez, vous avez juste à être clair sur ce qui est muable et l'ordre dans lequel les choses se passent; et de l'efficacité de côté, presque n'importe quel objet mutable pu construire et retourner une nouvelle, "mise à jour" de l'instance au lieu de changer son propre état interne.

Le plus gros problème ici est formalismes mathématiques, en particulier la forte utilisation de types de données algébriques dans une langue peu à l'écart du lambda calcul. Vous avez marqués ce avec Haskell et F#, mais rends compte que c'est seulement la moitié de la programmation fonctionnelle de l'univers; le Lisp de la famille est très différent, beaucoup plus libre de caractères par rapport à ML-style de langues. La plupart OO des systèmes en usage aujourd'hui sont très informelles dans la nature--formalismes existent pour OO mais ils ne sont pas appelés explicitement la façon FP formalismes sont dans le style de ML langues.

De nombreux conflits apparents disparaissent tout simplement si vous supprimez le formalisme des mésappariements. Voulez construire un flexible, dynamique, ponctuelle OO système sur le dessus d'un Lisp? Aller de l'avant, cela fonctionnera parfaitement. Souhaitez ajouter un officialisé, immuable OO système à un style de ML de la langue? Pas de problème, ne vous attendez pas à jouer gentiment avec .NET ou Java.


Maintenant, vous demandez peut-être, ce qui est un formalisme approprié pour la programmation orientée objet? Eh bien, voici la réponse: Dans beaucoup de façons, c'est plus de la fonction centrée sur que le style de ML FP! Je vais le renvoyer à l'un de mes préférés papiers de ce qui semble être la clé de la distinction: les données structurées comme des types de données algébriques dans le style de ML langues fournir une représentation concrète des données et de la capacité à définir des opérations sur elle; les objets de fournir une "boîte noire" de l'abstraction sur le comportement et la capacité de facilement remplacer des composants.

Il y a une dualité ici qui va au plus profond que juste FP vs POO: Il est étroitement lié à ce que certains langage de programmation théoriciens appellent l'Expression Problème: Avec des données concrètes, vous pouvez facilement ajouter de nouvelles opérations qui fonctionnent avec elle, mais en changeant les données de la structure est plus difficile. Avec des objets, vous pouvez facilement ajouter de nouvelles données (par exemple, de nouvelles sous-classes), mais l'ajout de nouvelles opérations est difficile (pense que l'ajout d'une nouvelle méthode abstraite d'une classe de base avec de nombreux descendants).

La raison pour laquelle je dis que la POO est plus fonction-centric est que les fonctions qui elles-mêmes représentent une forme de comportement de l'abstraction. En fait, vous pouvez simuler OO-le style de la structure dans quelque chose comme Haskell en utilisant les enregistrements de la tenue d'un tas de fonctions comme des objets, de laisser le type d'enregistrement, une "interface" ou "classe de base abstraite" de toutes sortes, et d'avoir des fonctions qui créent des dossiers de remplacer les constructeurs de classe. Donc, dans ce sens, OO les langues utilisent des fonctions d'ordre supérieur, de loin, bien plus souvent que, disons, Haskell serait.

Pour un exemple de quelque chose comme ce type de conception fait d'une très belle utilisation en Haskell, lire les sources pour le graphisme de drawingcombinators paquet, en particulier la façon dont il utilise opaque type d'enregistrement contenant des fonctions et combine les choses qu'en fonction de leur comportement.


EDIT: les quelques choses que j'ai oublié de mentionner ci-dessus.

Si OO en effet fait un large usage des fonctions d'ordre supérieur, il pourrait sembler à première vue qu'il doit être très naturellement dans un langage fonctionnel comme Haskell. Malheureusement, ce n'est pas tout à fait le cas. Il est vrai que les objets que j'ai décrit (cf. le document mentionné dans le LtU lien) a mis en place. en fait, le résultat est une pure OO style que la plupart des langages à objets, parce que "les membres privés" sont représentés par les valeurs cachées par la fermeture est utilisé pour construire la "objet" et sont inaccessibles à rien d'autre que l'instance spécifique lui-même. Vous n'obtenez pas beaucoup plus privé que ça!

Ce qui ne fonctionne pas très bien en Haskell est de sous-typage. Et, bien que je pense de l'héritage et sous-typage sont trop souvent mal utilisé dans les langages à objets, une certaine forme de sous-typage est assez utile pour être en mesure de combiner des objets de manière flexible. Haskell manque inhérents à la notion de sous-typage, et roulé à la main remplacements ont tendance à être extrêmement maladroit de travailler avec.

En aparté, la plupart des langages à objets statiques de type systèmes d'effectuer un hachage de sous-typage aussi bien par être trop laxiste avec la substituabilité et de ne pas fournir un soutien adéquat de la variance dans les signatures de méthodes. En fait, je pense que la seule part entière OO langue qui n'a pas planté complètement, au moins, que je sache, est Scala (F# semblait faire trop de concessions .NET, mais au moins, je ne pense pas qu'il en fait, de nouvelles erreurs). J'ai peu d'expérience avec de nombreux de ces langues, si, si je pourrais certainement être mal ici.

Sur un Haskell-note spécifique, ses "classes de type" regarde souvent tentant de OO programmeurs, à qui je dis: Ne pas y aller. En essayant de mettre en œuvre la programmation orientée objet que de façon terminera dans les larmes. Pensez à des classes de type comme un remplacement pour les fonctions surchargées/opérateurs, pas de la POO.

8voto

Heatsink Points 494

Comme pour Haskell, les classes sont de moins en moins utile car certains OO caractéristiques sont plus facilement atteints par d'autres moyens.

L'Encapsulation ou de données "cacher" est fréquemment effectuées par les fermetures de fonction ou existentielle types, plutôt que les membres privés. Par exemple, ici, est un type de données du générateur de nombre aléatoire avec encapsulé état. Le RNG contient une méthode pour générer des valeurs et une valeur de départ. Parce que le type de "graine" est encapsulé, la seule chose que vous pouvez faire, c'est de passer à la méthode.

les données RNG un où RNG :: (seed -> (a, semences)) -> graines> RNG un

Dynamique de la répartition de méthode dans le contexte de polymorphisme paramétrique ou "programmation générique" est fourni par les classes de type (qui ne sont pas classes OO). Un type de classe est comme un OO classe virtuelle de la méthode de la table. Cependant, il n'y a pas de données cacher. Les classes de Type ne leur "appartiennent" à un type de données de la façon dont les méthodes de la classe faire.

les données de Coordonner = C Int Int

exemple Eq Coordonner où C a b == C d e = a == b && j == e

Dynamique de la répartition de méthode dans le contexte de sous-typage polymorphisme ou "sous-classement" est presque une traduction de la classe pattern en Haskell à l'aide des enregistrements et des fonctions.

-- "Une classe de base abstraite" avec deux "méthodes virtuelles"
Objet de données =
Objet
 { draw :: Image -> IO ()
 , traduire :: Coord -> Objet
}

-- Un "constructeur de sous-classe"
le cercle de centre rayon = Objet draw_circle translate_circle
où
 -- la "sous-classe des méthodes"
 translate_circle rayon centre offset = cercle (centre + offset) de rayon
 draw_circle rayon centre de l'image = ...

6voto

Tomas Petricek Points 118959

Je pense qu'il y a plusieurs façons de comprendre la programmation orientée objet, les moyens. Pour moi, il n'est pas à propos de l'encapsulation mutable état, mais plus sur l'organisation et la structuration des programmes. Cet aspect de la programmation orientée objet peut être utilisé parfaitement bien en conjonction avec FP concepts.

Je crois que le mélange des deux concepts en F# est une approche très utile - vous pouvez associer immuable de l'état avec les opérations de travail sur cet état. Vous obtiendrez les fonctionnalités de dot achèvement des identificateurs, la capacité à les utiliser facilement, F# de code C#, etc., mais vous pouvez toujours faire votre code parfaitement fonctionnel. Par exemple, vous pouvez écrire quelque chose comme:

type GameWorld(characters) = 
  let calculateSomething character = 
    // ...
  member x.Tick() = 
    let newCharacters = characters |> Seq.map calculateSomething
    GameWorld(newCharacters)

Au début, les gens n'ont pas l'habitude de déclarer des types en F#, - vous pouvez juste commencer par l'écriture de fonctions et, plus tard, de faire évoluer votre code à utiliser (quand vous mieux comprendre le domaine et de savoir quelle est la meilleure façon de structurer le code). L'exemple ci-dessus:

  • Est encore purement fonctionnelle (l'état est une liste de caractères et il n'est pas muté)
  • Il est orienté objet - la seule chose inhabituelle, c'est que toutes les méthodes de renvoyer une nouvelle instance de "le monde"

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X