J’essaie de comprendre les protocoles de clojure et ce qu’ils sont censés pour résoudre de problème. Quelqu'un a-t-il une explication claire des quoi et le pourquoi des protocoles de clojure ?
Réponses
Trop de publicités?Le but de Protocoles en Clojure est de résoudre le Problème de l'Expression d'une manière efficace.
Alors, quelle est l'Expression du Problème? Il se réfère à la base du problème de l'extensibilité: nos programmes de manipuler des types de données à l'aide des opérations. Comme nos programmes d'évoluer, nous avons besoin de les étendre à de nouveaux types de données et à de nouvelles opérations. Et surtout, nous voulons être en mesure d'ajouter de nouvelles opérations qui travaillent avec les données existantes types, et nous voulons ajouter de nouveaux types de données qui travaillent avec les activités existantes. Et nous voulons que cela soit vrai, extension, c'est à dire que nous ne voulons pas modifier l' existant , nous voulons respecter les abstractions, nous voulons que nos extensions de modules distincts, dans des espaces de noms, compilé séparément, séparément déployé, séparément type cochée. Nous voulons qu'ils soient de type sécurisé. [Note: pas tous ces sens dans toutes les langues. Mais, par exemple, le but d'avoir leur type-safe de sens, même dans une langue comme Clojure. Tout simplement parce que nous ne pouvons pas statiquement vérifier le type de sécurité ne veut pas dire que nous voulons que notre code au hasard de pause, non?]
L'Expression Problème est, comment faites-vous pour fournir de tels extensibilité dans une langue?
Il s'avère que pour les naïfs, les implémentations de la procédure et/ou de la programmation fonctionnelle, il est très facile d'ajouter de nouvelles opérations (procédures, fonctions), mais très difficile d'ajouter de nouveaux types de données, depuis le fond, le travail sur les opérations avec les types de données à l'aide d'une sorte de cas de discrimination (switch
, case
, correspondant à un modèle) et vous avez besoin d'ajouter de nouveaux cas, c'est à dire modifier le code existant.
Et pour les naïfs OO, vous avez exactement le problème inverse: il est facile d'ajouter de nouveaux types de données qui travaillent avec les opérations existantes (en hériter ou de leur remplaçant), mais il est difficile d'ajouter de nouvelles opérations, depuis que, fondamentalement signifie en modifiant les classes/objets.
Plusieurs langues ont plusieurs constructions pour résoudre le Problème de l'Expression: Haskell a typeclasses, Scala a implicites arguments, Raquette a des Unités, Aller a des Interfaces, CLOS et Clojure ont Multimethods. Il y a aussi des "solutions" qui tentent de le résoudre, mais ne parviennent pas d'une manière ou d'une autre: les Interfaces et les Méthodes d'Extension en C# et Java, Monkeypatching en Ruby, Python, ECMAScript.
Notez que Clojure déjà a un mécanisme pour résoudre le Problème de l'Expression: Multimethods. Le problème OO a avec le PE, c'est qu'ils bundle opérations et les types ensemble. Avec Multimethods ils sont séparés. Le problème que le PF a, c'est qu'ils regroupent le fonctionnement et les cas de discrimination à l'ensemble. Encore une fois, avec Multimethods ils sont séparés.
Donc, nous allons comparer les Protocoles avec les Multimethods, puisque les deux font la même chose. Ou, pour le dire d'une autre façon: Pourquoi des Protocoles si nous avons déjà avoir Multimethods?
La principale chose Protocoles offrent plus de Multimethods est de Regroupement: vous pouvez regrouper de multiples fonctions ensemble et se dire "ces 3 fonctions de l' ensemble du formulaire de Protocole d' Foo
". Vous ne pouvez pas le faire avec Multimethods, ils se tiennent sur leur propre. Par exemple, vous pourriez déclarer qu'un Stack
Protocole consiste à la fois un push
et pop
fonctionner ensemble.
Alors, pourquoi ne pas simplement ajouter la possibilité de regrouper les Multimethods ensemble? Il y a une approche purement pragmatique de la raison, et c'est pourquoi utilisé le mot "efficace" dans ma phrase d'introduction: la performance.
Clojure est hébergé langue. I. e. il est spécifiquement conçu pour fonctionner sur une autre langue de la plate-forme. Et il s'avère que pratiquement n'importe quelle plate-forme que vous souhaitez Clojure pour s'exécuter sur (JVM, CLI, ECMAScript, Objective-C) a spécialisé de haute performance support pour la distribution uniquement sur le type de seulement le premier argument. Clojure Multimethods otoh, que l'expédition sur les propriétés arbitraires de tous les arguments.
Ainsi, les Protocoles de restreindre vous d'expédition seulement sur le premier argument et le seul de son type (ou comme un cas spécial sur nil
).
Ce n'est pas une limitation sur l'idée de Protocoles de soi, c'est un choix pragmatique pour accéder à l'optimisation des performances de la plateforme sous-jacente. En particulier, cela signifie que les Protocoles ont un trivial de cartographie de la JVM/CLI Interfaces, ce qui les rend très rapide. Assez rapidement, en fait, pour être en mesure de réécrire les parties de Clojure qui sont actuellement écrits en Java ou C# en Clojure lui-même.
Clojure a effectivement déjà eu des Protocoles depuis la version 1.0: Seq
est un Protocole, par exemple. Mais jusqu'à 1.2, vous ne pouviez pas écrire des Protocoles en Clojure, vous avez eu à les écrire dans la langue du pays d'accueil.
Je le trouve plus utile de penser à des protocoles comme étant conceptuellement similaire à une "interface" dans les langages orientés objet tels que Java. Un protocole définit un ensemble abstrait de fonctions qui peuvent être mis en œuvre de manière concrète pour un objet donné.
Un exemple:
(defprotocol my-protocol
(foo [x]))
Définit un protocole avec une fonction qui s'appelle "toto" qui agit sur un paramètre "x".
Vous pouvez ensuite créer des structures de données qui implémente le protocole, par exemple
(defrecord constant-foo [value]
my-protocol
(foo [x] value))
(def a (constant-foo. 7))
(foo a)
=> 7
Notez qu'ici, l'objet de la mise en œuvre du protocole est passé comme premier paramètre x
- un peu comme l'implicite "ce" paramètre dans les langages orientés objets.
L'un des très puissant et des fonctionnalités utiles de protocoles est que vous pouvez les étendre à des objets , même si l'objet n'a pas été conçu à l'origine pour soutenir le protocole. par exemple, vous pouvez prolonger le protocole ci-dessus à la java.lang.Chaîne de classe si vous le souhaitez:
(extend-protocol my-protocol
java.lang.String
(foo [x] (.length x)))
(foo "Hello")
=> 5