2017-08-17 6 views
2

pensée fonctionnelle comme, mais dans Clojure, ce qui est mieux, plus performatic et moins lourd à la machine virtuelle JavaClojure laisser vs multi-arité

(defn- on-message 
    ([options ch {:keys [headers delivery-tag]} ^bytes payload ^CompanyProto$Company$Builder company] 
    (check-id company) 
    (save company options) 
    (basic/ack ch delivery-tag)) 
    ([options ch ^PersistentHashMap kwargs ^bytes payload] 
    (on-message options 
       ch 
       kwargs 
       payload 
       (-> (CompanyProto$Company/newBuilder) 
        (.mergeFrom payload))))) 

ou

(defn- on-message [options ch {:keys [headers delivery-tag] ^bytes payload}] 
    (let [company (-> (CompanyProto$Company/newBuilder) (.mergeFrom payload))] 
    (check-id company) 
    (save company options) 
    (basic/ack ch delivery-tag))) 
+1

Y a-t-il une raison pour laquelle vous envisagez la première option avec multi-arité? A moins que la méthode ne soit appelée ailleurs avec les deux signatures, la seconde (avec let binding) est certainement moins encombrée. Je crois que vous économisez également un appel dans la pile, ce qui semble être un saut inutile dans le multi-arité. –

+0

@ToniVanhala Je l'ai fait avec laissez d'abord, mais un ami a dit que let est mauvais et pas fonctionnel (il est un développeur erlang), je suis nouveau dans les langes fonctionnels ... l'autre chose qu'il a dit est que c'est clojure n'est pas empilée –

+0

[L'expression "let" peut être considérée comme une abstraction lambda appliquée à une valeur] (https://en.wikipedia.org/wiki/Let_expression), donc elle correspond bien à la programmation fonctionnelle. –

Répondre

0

La réponse à votre question directe est qu'il n'y a pas de surcharge introduite dans un appel de fonction simplement parce que cette fonction a plusieurs arités. Dans Clojure, tous les appels de fonction sont compilés dans un appel de méthode. La méthode exacte invoquée dépend du nombre d'arguments - en ce qui concerne la JVM, chaque arité est compilée comme une méthode différente. La détermination de la méthode à appeler est effectuée au moment de la compilation, donc il n'y a pas de surcharge au moment de l'exécution. Ceci étant dit, il y a une surcharge pour faire des appels de fonction imbriqués. Plus précisément, chaque appel augmente la pile d'appels d'une quantité constante jusqu'à ce que cet appel retourne. Cependant, ces frais généraux sont minimes et peu susceptibles d'avoir un impact mesurable sur la performance dans ce cas.

@ToniVanhala je l'ai fait avec laisser d'abord, mais un ami a dit que let est mauvais et non fonctionnel (il est un développeur de Erlang), je suis nouveau dans Langs fonctionnels ... autre chose qu'il dit est que c'est étrange que clojure est pas empilable

Cela semble être la vraie question. Donc ça vaut la peine d'aborder ça.

let est simplement une expression qui nous permet de lier des valeurs à des variables. Ces liaisons sont immuables. En outre, il est bien connu que let peut être implémenté comme une macro-abstraction sur lambda (ou, dans le vocabulaire de Clojure, fn). Donc, il n'y a certainement rien à propos de let qui le rend "pas fonctionnel".

Il n'y a rien de "bizarre" ou de déroutant que Clojure ne soit pas sans pile. Clojure est un langage JVM et une pile d'appels est profondément intégrée dans le modèle de calcul abstrait de la JVM. Bien qu'il existe des moyens de contourner ce problème (par exemple, le style de continuation-passing), ces méthodes nécessitent soit l'abandon de l'interopérabilité JVM profonde, soit le paiement d'une pénalité de performance. Clojure est avant tout un langage pragmatique, et c'est une concession pragmatique. En outre, Clojure (être un Lisp) est vraiment un cadre de langage avec lequel vous forgez la langue que vous voulez. Votre langue peut faire des compromis différents. Par exemple, votre langue peut être "stackless". Ou il pourrait avoir first-class continuations (avertissement: je suis l'auteur de pulley.cps). Ou il peut avoir un completely different paradigm (c'est-à-dire, logique vs fonctionnel). Mais cela peut aussi être très simple. Avec Clojure, vous pouvez choisir ce que vous payez. Ironiquement, votre ami a raison de dire que le code n'est pas fonctionnel. Cependant, ce n'est pas à cause du let. Plutôt, à peu près tout sauf le let n'est pas fonctionnel.Par exemple, (save company options) est clairement une opération à effet secondaire. Cependant, cela ne signifie pas nécessairement que votre code est mauvais - même le programme fonctionnel le plus pur doit, à un moment donné, interagir avec le monde réel s'il veut avoir une valeur pratique.

+0

Voilà une réponse complète, merci pour cela, je voudrais savoir si vous pouvez montrer quelques exemples, comme je l'ai dit je suis nouveau à fonctionnel, et je veux vraiment l'apprendre, je n'ai pas compris comment sauver n'est pas fonctionnelle là-bas ... –

+0

@ RafaelCustódio, programmation de programmation fonctionnelle "pure" est sans effets secondaires. Si vous y pensez, «save»/doit/être soit à effet secondaire ou l'appel est entièrement superflu et devrait être éliminé. La raison en est simple (mais pas forcément immédiatement évidente) - la valeur renvoyée par 'save' n'est jamais utilisée. Par conséquent, l'appel à 'save' n'a de sens que s'il effectue un effet secondaire (comme stocker quelque chose dans une base de données). (suite) –

+0

Encore une fois (et je ne peux pas le souligner assez), je ne dis pas que votre code est mauvais est en aucune façon. En fait, je dirais que cela semble assez raisonnable - vous ne pouvez pas écrire dans une base de données sans effets secondaires. Mais cela signifie que votre code n'est techniquement pas "fonctionnel" (dans le sens le plus pur). J'espère que cela pourra aider. –

1

Si vous développez avec Java interop, définissez *warn-on-reflection* sur true. S'il y a des avertissements, vous devez l'éliminer avec des astuces de type. Ensuite, vous obtenez un code "perfomatique et moins lourd pour la JVM".

P.S. L'informatique numérique avec mutabilité, les types JVM simples et les débordements non vérifiés est l'autre histoire, si c'est votre cas, vous devez également vérifier cela.

+0

Merci pour les conseils, puisque je travaille avec des classes Java, je le ferai, mais où? Dans Core.clj? –

+0

@ RafaelCustódio, '(set! * Warn-on-reflection * true)' dans un fichier source avec ces fonctions. Faites aussi cet espace de noms avec '(: gen-class)'. – fevgenym

+0

Merci @fevgenym –