Question

Je ne pense pas avoir encore boudé le curry. Je comprends ce que ça fait et comment le faire. Je ne peux tout simplement pas penser à une situation qui me plairait.

Où utilisez-vous le curry en JavaScript (ou où les principales bibliothèques l'utilisent)? Les exemples de manipulation de DOM ou de développement d’applications générales sont les bienvenus.

Une des réponses mentionne l'animation. Des fonctions comme slideUp , fadeIn prennent un élément en tant qu'argument et sont normalement une fonction curry renvoyant la fonction d'ordre élevé avec la fonction d'animation & # 8220; par défaut & # 8221; intégré Pourquoi est-ce mieux que d’appliquer la fonction en haut avec quelques valeurs par défaut?

Y at-il des inconvénients à l’utiliser?

Comme demandé, voici de bonnes ressources sur JavaScript currying:

Je vais en ajouter d'autres au fur et à mesure qu'ils apparaissent dans les commentaires.

Ainsi, selon les réponses, le currying et l’application partielle sont en général des techniques de commodité.

Si vous affinez & # 8220; fréquemment & # 8221; une fonction de haut niveau en l’appelant avec la même configuration, vous pouvez curry (ou utiliser une partie de Resig) la fonction de niveau supérieur pour créer des méthodes d’aide simples et concises.

Était-ce utile?

La solution

@Hank Gay

En réponse au commentaire d'EmbiggensTheMind:

Je ne vois pas de cas où curry & # 8212; par lui-même & # 8212 ; est utile en JavaScript; Il s'agit d'une technique permettant de convertir des appels de fonction avec plusieurs arguments en chaînes d'appels de fonction avec un seul argument pour chaque appel, mais JavaScript prend en charge plusieurs arguments dans un seul appel de fonction.

Dans JavaScript & # 8212; et je suppose que la plupart des autres langages actuels (pas le lambda calcul) & # 8212; il est généralement associé à une application partielle, cependant. John Resig explique mieux , mais l'essentiel est qu'il existe une certaine logique cela s'appliquera à deux arguments ou plus, et vous ne connaîtrez que la ou les valeurs de certains de ces arguments.

Vous pouvez utiliser une application / un curry partiel pour corriger ces valeurs connues et renvoyer une fonction n'acceptant que les inconnus, qui sera invoquée ultérieurement lorsque vous avez réellement les valeurs que vous souhaitez transmettre. Ceci fournit un moyen astucieux d’éviter de vous répéter alors que vous auriez appelé les mêmes éléments JavaScript encore et encore avec toutes les mêmes valeurs, sauf une. Pour voler l'exemple de John:

String.prototype.csv = String.prototype.split.partial(/,\s*/);
var results = "John, Resig, Boston".csv();
alert( (results[1] == "Resig") + " The text values were split properly" );

Autres conseils

Voici un une utilisation intéressante ET pratique du curry en JavaScript utilisant des fermetures :

function converter(toUnit, factor, offset, input) {
    offset = offset || 0;
    return [((offset + input) * factor).toFixed(2), toUnit].join(" ");
}

var milesToKm = converter.curry('km', 1.60936, undefined);
var poundsToKg = converter.curry('kg', 0.45460, undefined);
var farenheitToCelsius = converter.curry('degrees C', 0.5556, -32);

milesToKm(10);            // returns "16.09 km"
poundsToKg(2.5);          // returns "1.14 kg"
farenheitToCelsius(98);   // returns "36.67 degrees C"

Cela repose sur une extension curry de Function , bien que, comme vous pouvez le voir, il utilise uniquement apply (rien de trop chic):

Function.prototype.curry = function() {
    if (arguments.length < 1) {
        return this; //nothing to curry with - return function
    }
    var __method = this;
    var args = toArray(arguments);
    return function() {
        return __method.apply(this, args.concat([].slice.apply(null, arguments)));
    }
}

J'ai trouvé des fonctions qui ressemblent au functools.partial de python plus utiles en JavaScript:

function partial(fn) {
  return partialWithScope.apply(this,
    Array.prototype.concat.apply([fn, this],
      Array.prototype.slice.call(arguments, 1)));
}

function partialWithScope(fn, scope) {
  var args = Array.prototype.slice.call(arguments, 2);
  return function() {
    return fn.apply(scope, Array.prototype.concat.apply(args, arguments));
  };
}

Pourquoi voudriez-vous l'utiliser? Une situation courante dans laquelle vous souhaitez utiliser ceci est lorsque vous souhaitez lier this dans une fonction à une valeur:

var callback = partialWithScope(Object.function, obj);

Maintenant, lorsque le rappel est appelé, this pointe sur obj . Ceci est utile dans les situations d’événement ou pour économiser de l’espace car il raccourcit généralement le code.

Le curry est similaire au partiel, à la différence que la fonction retournée n'accepte qu'un argument (si je comprends bien).

Convenir avec Hank Gay - C’est extrêmement utile dans certains langages de programmation véritablement fonctionnels - car c’est une partie nécessaire. Par exemple, dans Haskell, vous ne pouvez tout simplement pas prendre plusieurs paramètres dans une fonction - vous ne pouvez pas le faire en programmation fonctionnelle pure. Vous prenez un paramètre à la fois et construisez votre fonction. En JavaScript, cela est tout simplement inutile, malgré des exemples artificiels tels que "convertisseur". Voici le même code de convertisseur, sans nécessiter de currying:

var converter = function(ratio, symbol, input) {
    return (input*ratio).toFixed(2) + " " + symbol;
}

var kilosToPoundsRatio = 2.2;
var litersToUKPintsRatio = 1.75;
var litersToUSPintsRatio = 1.98;
var milesToKilometersRatio = 1.62;

converter(kilosToPoundsRatio, "lbs", 4); //8.80 lbs
converter(litersToUKPintsRatio, "imperial pints", 2.4); //4.20 imperial pints
converter(litersToUSPintsRatio, "US pints", 2.4); //4.75 US pints
converter(milesToKilometersRatio, "km", 34); //55.08 km

Je souhaite vivement que Douglas Crockford, dans "JavaScript: The Good Parts", ait évoqué l’histoire et l’utilisation actuelle du curry plutôt que ses propos désinvoltes. Après avoir lu cela, j'ai longtemps été bouleversé, jusqu'à ce que j'étudie la programmation fonctionnelle et réalise que c'est de là que ça vient.

Après un peu plus de réflexion, je pense qu’il existe un cas d’utilisation valable pour le curry en JavaScript: si vous essayez d’écrire en utilisant des techniques de programmation purement fonctionnelles utilisant JavaScript. Cela semble être un cas d'utilisation rare cependant.

Voici un exemple.

J'instrumentise plusieurs champs avec JQuery pour que je puisse voir ce que les utilisateurs font. Le code ressemble à ceci:

$('#foo').focus(trackActivity);
$('#foo').blur(trackActivity);
$('#bar').focus(trackActivity);
$('#bar').blur(trackActivity);

(Pour les utilisateurs non-JQuery, je dis que chaque fois que deux champs sont mis en évidence ou perdus, je souhaite que la fonction trackActivity () soit appelée. Je pourrais également utiliser une fonction anonyme. de le dupliquer 4 fois, donc je l'ai sorti et l'ai nommé.)

Maintenant, il s'avère qu'un de ces champs doit être traité différemment. J'aimerais pouvoir transmettre un paramètre à l'un de ces appels pour qu'il soit transmis à notre infrastructure de suivi. Avec currying, je peux.

Ce n'est pas de la magie ou quoi que ce soit ... juste un raccourci agréable pour les fonctions anonymes.

partial (alert, "FOO!") est équivalent à function () {alert ("FOO!");}}

.

partiel (Math.max, 0) correspond à function (x) {return Math.max (0, x);}

Les appels à la terminologie partielle ( MochiKit ). Je pense que d'autres bibliothèques donnent aux fonctions une méthode .curry qui fait de même. semble un peu plus agréable et moins bruyant que les fonctions anonymes.

En ce qui concerne les bibliothèques qui l'utilisent, il existe toujours des fonctionnalités .

Quand est-ce utile dans JS? Probablement aux mêmes moments, il est utile dans d’autres langues modernes, mais le seul moment où je peux me voir l’utiliser est associé à une application partielle.

Je dirais que, très probablement, toute la bibliothèque d’animation de JS utilise currying. Plutôt que de devoir transmettre à chaque appel un ensemble d’éléments impactés et une fonction décrivant le comportement de l’élément, en une fonction d’ordre supérieur assurant l’ensemble des données de minutage, généralement plus facile à libérer pour le client fonctionnent comme "slideUp", "fadeIn" qui prend uniquement des éléments en tant qu'arguments, et qui ne sont que des fonctions curries renvoyant la fonction d'ordre élevé avec la "fonction d'animation" par défaut. intégré.

Les fonctions JavaScript sont appelées lamda dans un autre langage fonctionnel. Il peut être utilisé pour composer une nouvelle API (fonction plus puissante ou complexe) sur la base de la saisie simple d'un autre développeur. Le curry n'est qu'une des techniques. Vous pouvez l'utiliser pour créer une API simplifiée permettant d'appeler une API complexe. Si vous êtes le développeur qui utilise l'API simplifiée (par exemple, vous utilisez jQuery pour effectuer des manipulations simples), vous n'avez pas besoin d'utiliser curry. Mais si vous voulez créer l’API simplifiée, Curry est votre ami. Vous devez écrire un framework javascript (comme jQuery, mootools) ou une bibliothèque, vous pourrez alors apprécier sa puissance. J'ai écrit une fonction curry améliorée à l'adresse http://blog.semanticsworks.com. /2011/03/enhanced-curry-method.html . Vous n'avez pas besoin de la méthode curry pour effectuer le curry, cela vous aide simplement de le faire, mais vous pouvez toujours le faire manuellement en écrivant une fonction A () {} pour renvoyer une autre fonction B () {}. Pour le rendre plus intéressant, utilisez la fonction B () pour retourner une autre fonction C ().

Je connais son ancien fil de discussion, mais je vais devoir montrer comment il est utilisé dans les bibliothèques javascript:

Je vais utiliser la bibliothèque lodash.js pour décrire concrètement ces concepts.

Exemple:

var fn = function(a,b,c){ 
return a+b+c+(this.greet || ‘'); 
}

Application partielle:

var partialFnA = _.partial(fn, 1,3);

Currying:

var curriedFn = _.curry(fn);

Liaison:

var boundFn = _.bind(fn,object,1,3 );//object= {greet: ’!'}

utilisation:

curriedFn(1)(3)(5); // gives 9 
or 
curriedFn(1,3)(5); // gives 9 
or 
curriedFn(1)(_,3)(2); //gives 9


partialFnA(5); //gives 9

boundFn(5); //gives 9!

différence:

après currying, nous obtenons une nouvelle fonction sans paramètres préalables.

après l'application partielle, nous obtenons une fonction liée à certains paramètres prebound.

dans la liaison, nous pouvons lier un contexte qui sera utilisé pour remplacer & # 8216; cette & # 8217 ;, sinon la valeur par défaut d'une fonction sera la fenêtre.

Conseil: Il n’est pas nécessaire de réinventer la roue. L'application partielle / la liaison / le curry sont très liés. Vous pouvez voir la différence ci-dessus. Utilisez ce sens n'importe où et les gens reconnaîtront ce que vous faites sans problèmes de compréhension et vous devrez utiliser moins de code.

Je conviens que vous souhaitez parfois lancer le processus en créant une pseudo-fonction qui aura toujours la valeur du premier argument renseigné. Heureusement, je suis tombé sur une toute nouvelle bibliothèque JavaScript appelée jPaq (h < un href = "http://jpaq.org/" rel = "nofollow"> ttp: //jpaq.org/ ) qui fournit cette fonctionnalité. La meilleure chose à propos de la bibliothèque est le fait que vous pouvez télécharger votre propre version qui ne contient que le code dont vous aurez besoin.

Je viens juste d’écrire un exemple jPaq qui montre quelques applications intéressantes de la fonction curry. Découvrez-le ici: Currying Up String Functions

Je voulais juste ajouter des ressources pour Functional.js:

Lecture / conférence expliquant certaines applications http://www.youtube.com/watch?v=HAcN3JyQoyY

Bibliothèque Functional.js mise à jour: https://github.com/loop-recur/FunctionalJS Quelques bons assistants (désolé nouvelle ici, pas de réputation: p): / loop-recur / PreludeJS

J'utilise beaucoup cette bibliothèque récemment pour réduire le nombre de répétitions dans une bibliothèque d'assistance client js IRC. C'est génial - aide vraiment à nettoyer et à simplifier le code.

De plus, si les performances deviennent un problème (mais cette librairie est assez légère), il est facile de simplement réécrire en utilisant une fonction native.

Vous pouvez utiliser la liaison native pour une solution rapide à une ligne

function clampAngle(min, max, angle) {
    var result, delta;
    delta = max - min;
    result = (angle - min) % delta;
    if (result < 0) {
        result += delta;
    }
    return min + result;
};

var clamp0To360 = clampAngle.bind(null, 0, 360);

console.log(clamp0To360(405)) // 45

Un autre coup dur: travailler avec des promesses.

(Avertissement: JS noob, venant du monde Python. Même dans ce cas, currying n'est pas très utilisé, mais il peut s'avérer utile à l'occasion. J'ai donc cribbed le currying fonction - voir les liens)

Tout d'abord, je commence par un appel ajax. En cas de succès, j'ai un traitement spécifique à effectuer, mais en cas d'échec, je souhaite simplement indiquer à l'utilisateur que le fait d'appeler quelque chose a entraîné une erreur . Dans mon code actuel, le message d'erreur est affiché dans un panneau de démarrage, mais j'utilise simplement la journalisation ici.

J'ai modifié mon URL en direct pour que cet échec se produise.

function ajax_batch(e){
    var url = $(e.target).data("url");

    //induce error
    url = "x" + url;

    var promise_details = $.ajax(
        url,
        {
            headers: { Accept : "application/json" },
            // accepts : "application/json",
            beforeSend: function (request) {
                if (!this.crossDomain) {
                    request.setRequestHeader("X-CSRFToken", csrf_token);
                }
        },
        dataType : "json",
        type : "POST"}
    );
    promise_details.then(notify_batch_success, fail_status_specific_to_batch);
}

Maintenant, ici pour indiquer à l'utilisateur qu'un lot a échoué, je dois écrire cette information dans le gestionnaire d'erreurs car il ne reçoit qu'une réponse du serveur.

Je n'ai toujours que les informations disponibles au moment du codage. Dans mon cas, j'ai un certain nombre de lots possibles, mais je ne sais pas lequel a échoué. l'analyse de la réponse du serveur à propos de l'URL ayant échoué.

function fail_status_specific_to_batch(d){
    console.log("bad batch run, dude");
    console.log("response.status:" + d.status);
}

Faisons-le. La sortie de la console est:

console:

mauvaise exécution par lots, mec utility.js (ligne 109) response.status: 404

Maintenant, changeons un peu les choses et utilisons un gestionnaire d'échec générique réutilisable, mais aussi un gestionnaire curried au moment de l'exécution, avec le contexte d'appel connu à la date du code et le moment d'exécution. informations disponibles sur l'événement.

    ... rest is as before...
    var target = $(e.target).text();
    var context = {"user_msg": "bad batch run, dude.  you were calling :" + target};
    var contexted_fail_notification = curry(generic_fail, context); 

    promise_details.then(notify_batch_success, contexted_fail_notification);
}

function generic_fail(context, d){
    console.log(context);
    console.log("response.status:" + d.status);
}

function curry(fn) {
     var slice = Array.prototype.slice,
        stored_args = slice.call(arguments, 1);
     return function () {
        var new_args = slice.call(arguments),
              args = stored_args.concat(new_args);
        return fn.apply(null, args);
     };
}

console:

Objet {user_msg = " mauvaise exécution par lots, mec. vous appeliez: Exécuter ACL maintenant &};} utility.js (ligne 117) état de réponse: 404 utility.js (ligne 118)

Plus généralement, compte tenu de l’utilisation généralisée des rappels dans JS, le curry semble être un outil très utile.

https://javascriptweblog.wordpress.com / 2010/04/05 / cuisson au curry - fonctions plus savoureuses / http: //www.drdobbs .com / open-source / currying-and-partial-functions-in-javasc / 231001821? pgno = 2

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top