Question Différence entre symboles et Vars dans Clojure


Je suis toujours un peu confus par Symbols et Vars dans Clojure. Par exemple, peut-on dire que + est un symbole utilisé pour désigner une variable, et cette variable pointe vers une valeur qui est une fonction pouvant ajouter des nombres?

Alors, que se passe-t-il, étape par étape, lorsque je saisis simplement «+» dans une REPL?

  1. Le symbole est qualifié pour un espace de noms, dans ce cas clojure.core
  2. Ensuite, dans certaines tables de symboles, il y a l'information qui fait référence à un var
  3. Lorsque cette variable est évaluée, le résultat est une valeur de fonction?

35
2018-02-02 13:44


origine


Réponses:


Il y a un symbole + dont vous pouvez parler en le citant:

user=> '+
+
user=> (class '+)
clojure.lang.Symbol
user=> (resolve '+)
#'clojure.core/+

Donc, cela se résume à # '+, qui est un Var:

user=> (class #'+)
clojure.lang.Var

Le Var référence l'objet fonction:

user=> (deref #'+)
#<core$_PLUS_ clojure.core$_PLUS_@55a7b0bf>
user=> @#'+
#<core$_PLUS_ clojure.core$_PLUS_@55a7b0bf>

(Le signe @ est simplement un raccourci pour deref.) Bien sûr, la manière habituelle d’obtenir la fonction est de ne pas citer le symbole:

user=> +
#<core$_PLUS_ clojure.core$_PLUS_@55a7b0bf>

Notez que les liaisons lexicales sont un mécanisme différent, et elles peuvent masquer Vars, mais vous pouvez les ignorer en vous référant explicitement au Var:

user=> (let [+ -] [(+ 1 2) (@#'+ 1 2)])
[-1 3]

Dans ce dernier exemple, le deref peut même être omis:

user=> (let [+ -] [(+ 1 2) (#'+ 1 2)])
[-1 3]

En effet, Var implémente IFn (interface pour les fonctions Clojure) en appelant deref sur lui-même, en convertissant le résultat en IFn et en lui déléguant l'appel de fonction.

Le mécanisme de visibilité utilisé lorsque vous définissez des fonctions privées avec defn est basé sur les métadonnées du symbole. Vous pouvez le contourner en vous référant directement au Var, comme ci-dessus:

user=> (ns foo)
nil
foo=> (defn- private-function [] :secret)
#'foo/private-function
foo=> (in-ns 'user)
#<Namespace user>
user=> (foo/private-function)
java.lang.IllegalStateException: var: #'foo/private-function is not public (NO_SOURCE_FILE:36)
user=> (#'foo/private-function)
:secret

58
2018-02-02 16:28



Voir la documentation des espaces de noms:

Les espaces de noms sont des mappages à partir de simples symboles (non qualifiés) vers Vars et / ou les classes. Vars peut être interné dans un espace de noms, à l'aide de def ou de l'une de ses variantes, auquel cas ils ont un symbole simple pour un nom et une référence à leur espace de nom. Un espace de noms peut également contenir des correspondances entre des symboles et des vars internés dans d'autres espaces de noms à l'aide de la référence ou de l'utilisation, ou de symboles dans des objets de classe en utilisant l'importation.

Donc, fondamentalement, vos étapes 1 et 2 sont unifiées: les espaces de noms sont les tables de symboles.

Et en ce qui concerne l'étape 3: j'aime la définition des variables qui sont des combinaisons de noms de valeurs. Le symbole est le nom de la variable et son évaluation entraînera sa valeur.


6
2018-02-02 15:00



Cette réponse n'est pas très différente des autres, elle ne suppose pas que vous souhaitiez initialement apprendre plusieurs nouvelles fonctions et concepts simplement pour comprendre ce qui se passe:

  1. + est un symbole assis clojure.core qui est accessible par défaut à votre code.
  2. Lorsqu'elle est utilisée dans votre code sans aucune intention très avancée, comme la citer ou découvrir sa classe, clojure cherchera le Var sur lequel elle pointe.
  3. Si ce Var est une fonction, quand + est utilisé dans la position de tête d'une liste, clojure va essayer d'appeler cette fonction (NullPointerException si ce Var est arrivé à ne pas pointer une fonction). Si fourni comme argument à une autre fonction, cette fonction peut faire la même chose pour l'appeler. C'est comment fonctionne l'appel de fonction.

autres commentaires à compléter:

La plupart ou toutes les langues utilisent des tables de symboles. En tant que langage quelque peu dynamique, Clojure utilise cette couche d'indirection supplémentaire (symbole → fonction Var → fonction plutôt que fonction symbole →) de sorte que la réécriture dynamique de la fonction associée à un symbole est plus réalisable et élégante, source de curiosité pour les débutants.

Comme d’autres réponses ont un peu trop insisté, vous pouvez sinon effectuer trucs de fantaisie comme le cite ('+) pour éviter son évaluation, ou même l'inspecter avec class et / ou resolve comme si vous étiez intéressé à vérifier ce que c'est (class), ou dans quel espace de noms il se trouve (resolve). Vous pouvez aussi toucher la variable pointée via via var ou #'. Vous faites généralement ces choses fantaisistes si vous écrivez des macros ou si vous êtes très expérimenté, en particulier lorsque vous travaillez dans le fichier de remplacement; En fonction du style dans lequel vous écrivez vos macros, vous pourriez en citer beaucoup.

et une illustration de fantaisie pour la personne extra-exploratoire:

Etant un langage quelque peu flexible, clojure expose api pour prendre le symbole → Var → marche de la fonction par vous-même. En général, vous ne le faites jamais pour utiliser une fonction, car cela serait évidemment ennuyeux et redondant, mais il peut être utilisé ici pour illustrer le processus:

(deref (resolve '+))

À savoir, le symbole est d'abord résolu à son Var, puis la chose que le Var pointe, est arrivé. Cela illustre simplement le processus en deux étapes pour arriver à une fonction (symbole → Var → fonction), ce qui se passe en coulisse. J'espère que vous avez évité de lire cette partie supplémentaire.


TL; DR

la réponse à la question initiale est simplement: oui.


2
2018-05-02 07:11