javascript - currification - currying es6
Curry JavaScript: ¿cuáles son las aplicaciones prácticas? (15)
No creo que haya asimilado el curry todavía. Entiendo lo que hace y cómo hacerlo. No puedo pensar en una situación en la que lo usaría.
¿Dónde está usando currying en JavaScript (o dónde están las principales bibliotecas que lo usan)? Ejemplos de manipulación de DOM o desarrollo de aplicaciones generales son bienvenidos.
Una de las respuestas menciona animación. Funciones como slideUp
, fadeIn
toman un elemento como argumentos y normalmente son una función curried que devuelve la función de orden superior con la función de animación predeterminada incorporada. ¿Por qué es mejor que simplemente aplicar la función ascendente con algunos valores predeterminados?
¿Hay algún inconveniente para usarlo?
Como se solicita aquí hay algunos buenos recursos sobre el currying de JavaScript:
- http://www.dustindiaz.com/javascript-curry/
- Crockford, Douglas (2008) JavaScript: Las buenas partes
- http://www.svendtofte.com/code/curried_javascript/ (Toma un desvío en ML así que omita toda la sección de "Un curso intensivo en ML" y comience nuevamente en "Cómo escribir el curry JavaScript")
- http://blog.morrisjohns.com/javascript_closures_for_dummies
- ¿Cómo funcionan los cierres de JavaScript?
- http://ejohn.org/blog/partial-functions-in-javascript (Sr. Resig en el dinero como de costumbre)
- http://benalman.com/news/2010/09/partial-application-in-javascript/
Agregaré más a medida que surjan en los comentarios.
Entonces, según las respuestas, el currying y la aplicación parcial en general son técnicas de conveniencia.
Si con frecuencia "refina" una función de alto nivel llamándola con la misma configuración, puede curry (o usar la función parcial de Resig) la función de nivel superior para crear métodos de ayuda simples y concisos.
@Hank Gay
En respuesta al comentario de EmbiggensTheMind:
No puedo pensar en una instancia donde currying por sí mismo, sea útil en JavaScript; es una técnica para convertir llamadas a funciones con múltiples argumentos en cadenas de llamadas a función con un único argumento para cada llamada, pero JavaScript admite múltiples argumentos en una sola llamada a función.
En JavaScript, y supongo que en la mayoría de los demás idiomas (no en cálculo lambda), se asocia comúnmente con la aplicación parcial. John Resig lo explica mejor , pero lo esencial es que tiene alguna lógica que se aplicará a dos o más argumentos, y usted solo conoce los valores para algunos de esos argumentos.
Puede usar la aplicación / currying parcial para corregir esos valores conocidos y devolver una función que solo acepte las incógnitas, que se invocará más adelante cuando realmente tenga los valores que desea aprobar. Esto proporciona una manera ingeniosa de evitar repetirse cuando habría estado llamando a los mismos complementos de JavaScript una y otra vez con todos los mismos valores, excepto uno. Para robar el ejemplo de Juan:
String.prototype.csv = String.prototype.split.partial(/,/s*/);
var results = "John, Resig, Boston".csv();
alert( (results[1] == "Resig") + " The text values were split properly" );
Acabo de escribir un ejemplo de jPaq que muestra algunas aplicaciones geniales de la función de curry. Compruébalo aquí: Funciones de cadenas de Currying Up
Aquí hay un ejemplo.
Estoy instrumentando muchos campos con JQuery para poder ver lo que los usuarios están haciendo. El código se ve así:
$(''#foo'').focus(trackActivity);
$(''#foo'').blur(trackActivity);
$(''#bar'').focus(trackActivity);
$(''#bar'').blur(trackActivity);
(Para usuarios que no usan JQuery, digo que cada vez que un par de campos se ponen o pierden el foco, quiero que se llame a la función trackActivity (). También podría usar una función anónima, pero tendría que duplicarla 4 veces, así que lo saqué y lo nombré).
Ahora resulta que uno de esos campos debe manejarse de manera diferente. Me gustaría poder pasar un parámetro en una de esas llamadas para pasar a nuestra infraestructura de seguimiento. Con currying, puedo.
Aquí hay un uso interesante Y práctico de currying en JavaScript que utiliza cierres :
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"
Esto se basa en una extensión curry
de Function
, aunque como puede ver, solo usa apply
(nada demasiado sofisticado):
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))); } }
Diría que, probablemente, toda la biblioteca de animación en JS está usando currying. En lugar de tener que pasar para cada llamada, un conjunto de elementos impactados y una función que describe cómo debe comportarse el elemento, a una función de orden superior que garantizará todo el tiempo, generalmente es más fácil para el cliente, como API pública. funcionan como "slideUp", "fadeIn" que toma solo elementos como argumentos, y que son solo algunas funciones al curry que devuelven la función de orden alto con la "función de animación" predeterminada incorporada.
En cuanto a las bibliotecas que lo usan, siempre hay funcional .
¿Cuándo es útil en JS? Probablemente las mismas veces sea útil en otros idiomas modernos, pero el único momento en que puedo ver que lo uso es junto con la aplicación parcial.
Encontré funciones que se parecen a los functools.partial
de functools.partial
más útil 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));
};
}
¿Por qué querrías usarlo? Una situación común en la que desea utilizar esto es cuando desea vincular this
en una función a un valor:
var callback = partialWithScope(Object.function, obj);
Ahora cuando se llama a la devolución de llamada, this
apunta a obj
. Esto es útil en situaciones de eventos o para ahorrar espacio porque generalmente hace que el código sea más corto.
Currying es similar a partial con la diferencia de que la función que devuelve currying acepta un argumento (por lo que yo entiendo).
Estar de acuerdo con Hank Gay: es extremadamente útil en ciertos lenguajes de programación funcionales verdaderos, porque es una parte necesaria. Por ejemplo, en Haskell simplemente no puede tomar múltiples parámetros para una función: no puede hacer eso en la programación funcional pura. Toma un parámetro a la vez y desarrolla su función. En JavaScript, simplemente no es necesario, a pesar de ejemplos artificiales como "convertidor". Aquí está el mismo código de convertidor, sin la necesidad 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
Tengo muchas ganas de que Douglas Crockford, en "JavaScript: The Good Parts", haya mencionado un poco la historia y el uso real del currying en lugar de sus comentarios espontáneos. Durante mucho tiempo después de leer eso, me quedé boquiabierto, hasta que estuve estudiando la programación funcional y me di cuenta de que venía de allí.
Después de pensar un poco más, postulo que hay un caso de uso válido para currying en JavaScript: si estás tratando de escribir usando técnicas de programación puramente funcionales usando JavaScript. Parece un caso de uso raro sin embargo.
Estoy de acuerdo en que a veces le gustaría poner en marcha la bola creando una pseudo-función que siempre tendrá el valor del primer argumento completado. Afortunadamente, me encontré con una nueva biblioteca de JavaScript llamada jPaq (h ttp://jpaq.org/ ) que proporciona esta funcionalidad. Lo mejor de la biblioteca es el hecho de que puedes descargar tu propia compilación que contiene solo el código que necesitarás.
Las funciones de JavaScript se llaman lamda en otro lenguaje funcional. Se puede usar para componer una nueva API (función más completa o complementaria) en función de la entrada simple de otro desarrollador. Curry es solo una de las técnicas. Puede usarlo para crear una API simplificada para llamar a una API compleja. Si usted es el desarrollador que usa la API simplificada (por ejemplo, usa jQuery para hacer una manipulación simple), no necesita usar curry. Pero si quieres crear la API simplificada, Curry es tu amigo. Tienes que escribir un marco de JavaScript (como jQuery, mootools) o una biblioteca, entonces puedes apreciar su poder. Escribí una función mejorada de curry, en http://blog.semanticsworks.com/2011/03/enhanced-curry-method.html . No necesita el método de curry para currar, solo ayuda a currar, pero siempre puede hacerlo manualmente escribiendo una función A () {} para devolver otra función B () {}. Para hacerlo más interesante, use la función B () para devolver otra función C ().
No es magia ni nada ... solo una taquigrafía agradable para funciones anónimas.
parcial (alerta, "¡FOO!") es equivalente a la función () {alerta ("FOO!");}
parcial (Math.max, 0) corresponde a la función (x) {return Math.max (0, x);}
Las llamadas a parcial (terminología de MochiKit . Creo que algunas otras bibliotecas le dan a las funciones un método de currículum que hace lo mismo) se ven un poco más agradables y menos ruidosas que las funciones anónimas.
Otra puñalada, trabajar con promesas.
(Descargo de responsabilidad: novato JS, procedente del mundo de Python. Incluso allí, currying no se usa mucho, pero puede ser útil en alguna ocasión. Así que compré la función Currying - ver enlaces)
Primero, estoy empezando con una llamada ajax. Tengo un procesamiento específico para hacer en cuanto al éxito, pero en caso de falla, solo quiero darle al usuario la opinión de que llamar a algo resultó en algún error . En mi código actual, muestro los comentarios de error en un panel de arranque, pero estoy usando el registro aquí.
Modifiqué mi URL en vivo para que esto fallara.
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);
}
Ahora, aquí para decirle al usuario que un lote falló, necesito escribir esa información en el controlador de errores, porque todo lo que recibe es una respuesta del servidor.
Todavía tengo la información disponible en el momento de la codificación; en mi caso, tengo una cantidad de lotes posibles, pero no sé cuál ha fallado al analizar la respuesta del servidor acerca de la URL fallida.
function fail_status_specific_to_batch(d){
console.log("bad batch run, dude");
console.log("response.status:" + d.status);
}
Vamos a hacerlo. La salida de la consola es:
consola:
bad batch run, dude utility.js (line 109) response.status:404
Ahora, cambiemos un poco las cosas y usemos un manejador de fallas genéricas reutilizables, pero también uno que esté currificado en tiempo de ejecución con el contexto de llamadas conocidas en el tiempo de código y la información de tiempo de ejecución disponible desde el evento.
... 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);
};
}
consola:
Object { user_msg="bad batch run, dude. you were calling :Run ACL now"} utility.js (line 117) response.status:404 utility.js (line 118)
En términos más generales, dado el uso generalizado de las retrollamadas en JS, el currículum parece ser una herramienta bastante útil.
https://javascriptweblog.wordpress.com/2010/04/05/curry-cooking-up-tastier-functions/ http://www.drdobbs.com/open-source/currying-and-partial-functions-in-javasc/231001821?pgno=2
Puede usar enlace nativo para una solución rápida de una línea
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
Sé su viejo hilo, pero tendré que mostrar cómo se usa esto en las bibliotecas de JavaScript:
Usaré la biblioteca lodash.js para describir estos conceptos de manera concreta.
Ejemplo:
var fn = function(a,b,c){
return a+b+c+(this.greet || ‘'');
}
Aplicación parcial:
var partialFnA = _.partial(fn, 1,3);
Zurra:
var curriedFn = _.curry(fn);
Unión:
var boundFn = _.bind(fn,object,1,3 );//object= {greet: ’!''}
uso:
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!
diferencia:
después de curried obtenemos una nueva función sin ningún parámetro pre ligado.
después de la aplicación parcial obtenemos una función que está vinculada con algunos parámetros prebound.
en el enlace, podemos enlazar un contexto que se usará para reemplazar ''esto''; si no, el valor predeterminado de cualquier función será el alcance de la ventana.
Aconseje: no hay necesidad de reinventar la rueda. La aplicación / encuadernación / currículo parciales están muy relacionadas. Puedes ver la diferencia anterior. Use este significado en cualquier lugar y la gente reconocerá lo que está haciendo sin problemas de comprensión, además tendrá que usar menos código.
Solo quería agregar algunos recursos para Functional.js:
Conferencia / conferencia que explica algunas aplicaciones http://www.youtube.com/watch?v=HAcN3JyQoyY
Biblioteca Funcional.js actualizada: https://github.com/loop-recur/FunctionalJS Algunos buenos ayudantes (lo siento nuevo aquí, sin reputación: p): / loop-recur / PreludeJS
He estado usando esta biblioteca mucho recientemente para reducir la repetición en una biblioteca auxiliar de clientes js IRC. Es genial, realmente ayuda a limpiar y simplificar el código.
Además, si el rendimiento se convierte en un problema (pero esta lib es bastante ligera), es fácil simplemente reescribir usando una función nativa.