Caril de JavaScript: quais são as aplicações práticas?

Eu não acho que estudei o curry ainda. Eu entendo o que isso faz e como fazê-lo. Eu não consigo pensar em uma situação que eu usaria.

Onde você está usando o currying em JavaScript (ou onde estão as principais bibliotecas que o usam)? A manipulação de DOM ou exemplos gerais de desenvolvimento de aplicativos são bem-vindos.

Uma das respostas menciona animação. Funções como slideUp , fadeIn tomam um elemento como argumentos e são normalmente uma function curry que retorna a function de alta ordem com a “function de animação” padrão embutida. Por que isso é melhor do que simplesmente aplicar a function maior com alguns padrões?

Existem algumas desvantagens em usá-lo?

Conforme solicitado, aqui estão alguns bons resources em currying de JavaScript:

  • http://www.dustindiaz.com/javascript-curry/
  • Crockford, Douglas (2008) JavaScript: as boas partes
  • http://www.svendtofte.com/code/curried_javascript/ (faz um desvio para o ML, pule toda a seção de “Um curso intensivo no ML” e comece de novo em “Como escrever o JavaScript curry”)
  • http://blog.morrisjohns.com/javascript_closures_for_dummies
  • Como funcionam os fechamentos de JavaScript?
  • http://ejohn.org/blog/partial-functions-in-javascript (Mr. Resig sobre o dinheiro, como de costume)
  • http://benalman.com/news/2010/09/partial-application-in-javascript/

Vou adicionar mais como eles surgem nos comentários.


Assim, de acordo com as respostas, o currying e a aplicação parcial em geral são técnicas de conveniência.

Se você está freqüentemente “refinando” uma function de alto nível chamando-a com a mesma configuração, você pode curry (ou usar parcial do Resig) a function de nível mais alto para criar methods auxiliares simples e concisos.

    @Hank Gay

    Em resposta ao comentário da EmbiggensTheMind:

    Não consigo pensar em uma instância em que o curry – por si só – seja útil em JavaScript; é uma técnica para converter chamadas de function com vários argumentos em cadeias de chamadas de function com um único argumento para cada chamada, mas o JavaScript suporta vários argumentos em uma única chamada de function.

    Em JavaScript – e assumo a maioria das outras linguagens reais (não lambda calculus) – é comumente associada à aplicação parcial, no entanto. John Resig explica melhor , mas a essência é que tem alguma lógica que será aplicada a dois ou mais argumentos, e você só sabe o (s) valor (es) para alguns desses argumentos.

    Você pode usar o aplicativo parcial / curry para corrigir esses valores conhecidos e retornar uma function que aceita apenas os desconhecidos, para ser invocada mais tarde quando você realmente tiver os valores que deseja passar. Isso fornece uma maneira bacana de evitar a repetição de si mesmo quando você estaria chamando os mesmos JavaScript embutidos repetidamente com todos os mesmos valores, exceto um. Para roubar o exemplo de João:

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

    Aqui está um uso interessante e prático de currying em JavaScript que usa closures :

     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" 

    Isso depende de uma extensão curry de Function , embora, como você pode ver, ele só use apply (nada muito chique):

     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))); } } 

    Eu encontrei funções que se assemelham a functools.partial do python mais útil em 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 que você iria querer usá-lo? Uma situação comum em que você deseja usar isso é quando você deseja associar this em uma function a um valor:

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

    Agora quando o retorno de chamada é chamado, this aponta para obj . Isso é útil em situações de events ou para economizar espaço, porque geralmente torna o código mais curto.

    O currying é semelhante ao partial com a diferença de que a function que o curry retorna apenas aceita um argumento (até onde eu entendo).

    Concordando com o Hank Gay – É extremamente útil em certas linguagens de programação funcionais verdadeiras – porque é uma parte necessária. Por exemplo, em Haskell você simplesmente não pode levar múltiplos parâmetros para uma function – você não pode fazer isso em pura functional programming. Você pega um param de cada vez e constrói sua function. Em JavaScript é simplesmente desnecessário, apesar de exemplos inventados como “conversor”. Aqui está o mesmo código do conversor, sem a necessidade de curry:

     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 

    Desejo muito que Douglas Crockford, em “JavaScript: The Good Parts”, tenha feito alguma menção à história e ao uso real do curry, em vez de suas observações improvisadas. Por muito tempo depois de ler isso, fiquei chocado, até que eu estava estudando functional programming e percebi que é de onde veio.

    Depois de pensar um pouco mais, afirmo que há um caso de uso válido para curry em JavaScript: se você estiver tentando escrever usando técnicas de functional programming pura usando JavaScript. Parece um caso de uso raro embora.

    Aqui está um exemplo.

    Eu estou instrumentando um monte de campos com JQuery para que eu possa ver o que os usuários estão fazendo. O código é assim:

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

    (Para usuários que não são do JQuery, estou dizendo que, a qualquer momento, alguns campos ficam ou perdem o foco, quero que a function trackActivity () seja chamada. Eu também poderia usar uma function anônima, mas teria que duplicá-la. 4 vezes, então eu peguei e nomeei.)

    Agora acontece que um desses campos precisa ser tratado de maneira diferente. Eu gostaria de poder passar um parâmetro em uma dessas chamadas para serem passadas para nossa infraestrutura de rastreamento. Com curry, eu posso.

    Não é mágica ou qualquer coisa … apenas uma abreviação agradável para funções anônimas.

    partial(alert, "FOO!") é equivalente a function(){alert("FOO!");}

    partial(Math.max, 0) corresponde à function(x){return Math.max(0, x);}

    As chamadas para parcial (terminologia MochiKit . Eu acho que algumas outras bibliotecas dão funções a um método .curry que faz a mesma coisa) parecem um pouco mais agradáveis ​​e menos barulhentas do que as funções anônimas.

    Quanto às bibliotecas que o utilizam, sempre existe o Functional .

    Quando é útil no JS? Provavelmente as mesmas vezes é útil em outras linguagens modernas, mas a única vez que eu posso me ver usando isso é em conjunto com a aplicação parcial.

    Eu diria que, provavelmente, toda a biblioteca de animação do JS está usando curry. Ao invés de ter que passar por cada chamada um conjunto de elementos impactados e uma function, descrevendo como o elemento deve se comportar, para uma function de ordem mais alta que garantirá todo o tempo, é geralmente mais fácil para o cliente liberar, como API pública. function como “slideUp”, “fadeIn” que leva apenas elementos como argumentos, e que são apenas algumas funções curried retornando a function de alta ordem com a “function de animação” padrão embutida.

    Funções JavaScript são chamadas de lamda em outra linguagem funcional. Ele pode ser usado para compor uma nova API (function mais poderosa ou complementar) com base na input simples de outro desenvolvedor. Curry é apenas uma das técnicas. Você pode usá-lo para criar uma API simplificada para chamar uma API complexa. Se você é o desenvolvedor que usa a API simplificada (por exemplo, você usa o jQuery para fazer uma manipulação simples), você não precisa usar o curry. Mas se você quiser criar o api simplificado, o curry é seu amigo. Você tem que escrever uma estrutura de javascript (como jQuery, mootools) ou biblioteca, então você pode apreciar o seu poder. Eu escrevi uma function de curry aprimorada, em http://blog.semanticsworks.com/2011/03/enhanced-curry-method.html . Você não precisa do método curry para fazer curry, apenas ajuda a fazer currying, mas você sempre pode fazê-lo manualmente escrevendo uma function A () {} para retornar outra function B () {}. Para torná-lo mais interessante, use a function B () para retornar outra function C ().

    Eu sei o seu segmento antigo, mas vou ter que mostrar como isso está sendo usado em bibliotecas javascript:

    Eu usarei a biblioteca lodash.js para descrever esses conceitos concretamente.

    Exemplo:

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

    Aplicação parcial:

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

    Currying:

     var curriedFn = _.curry(fn); 

    Obrigatório:

     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! 

    diferença:

    depois de curry, obtemos uma nova function sem parâmetros pré-bound.

    Após a aplicação parcial, obtemos uma function que é associada a alguns parâmetros pré-gravados.

    na binding, podemos ligar um contexto que será usado para replace ‘this’, se não o padrão vinculado de qualquer function será o escopo da janela.

    Aconselhar: Não há necessidade de reinventar a roda. Aplicação parcial / binding / curry estão muito relacionados. Você pode ver a diferença acima. Use este significado em qualquer lugar e as pessoas vão reconhecer o que você está fazendo sem problemas na compreensão e você terá que usar menos código.

    Eu concordo que às vezes você gostaria de dar o pontapé inicial criando uma pseudo-function que sempre terá o valor do primeiro argumento preenchido. Felizmente, me deparei com uma nova biblioteca JavaScript chamada jPaq (h ttp: // jpaq.org/ ) que fornece essa funcionalidade. A melhor coisa sobre a biblioteca é o fato de que você pode fazer o download de sua própria construção, que contém apenas o código que você precisará.

    Acabei de escrever um exemplo de jPaq que mostra algumas aplicações interessantes da function curry. Confira aqui: Currying Up String Functions

    Só queria adicionar alguns resources para o Functional.js:

    Palestra / conferência explicando algumas aplicações http://www.youtube.com/watch?v=HAcN3JyQoyY

    Biblioteca Functional.js atualizada: https://github.com/loop-recur/FunctionalJS Alguns bons ajudantes (desculpe, novidade aqui, sem reputação: p): / loop-recur / PreludeJS

    Eu tenho usado essa biblioteca muito recentemente para reduzir a repetição em uma biblioteca auxiliar de clientes do js IRC. É ótimo – realmente ajuda a limpar e simplificar o código.

    Além disso, se o desempenho se tornar um problema (mas esta lib é bastante leve), é fácil simplesmente rewrite usando uma function nativa.

    Você pode usar o bind nativo para uma solução rápida de uma linha

     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 

    Outra facada, de trabalhar com promises.

    (Isenção de responsabilidade: noob JS, vindo do mundo Python. Mesmo lá, o currying não é muito usado, mas pode vir a calhar em algumas ocasiões. Então, eu inventei a function curry – veja os links)

    Primeiro, eu estou começando com uma chamada de ajax. Eu tenho algum processamento específico para fazer no sucesso, mas no fracasso, eu só quero dar ao usuário o feedback que chamando algo resultou em algum erro . No meu código real, eu exibo o feedback de erro em um painel de bootstrap, mas estou apenas usando o log aqui.

    Modifiquei minha URL ao vivo para fazer isso falhar.

     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); } 

    Agora, aqui para dizer ao usuário que um lote falhou, eu preciso escrever essa informação no manipulador de erro, porque tudo o que está recebendo é uma resposta do servidor.

    Eu ainda só tenho a informação disponível no momento da codificação – no meu caso eu tenho um número de possíveis lotes, mas eu não sei qual deles falhou ao analisar a resposta do servidor sobre o URL com falha.

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

    Vamos fazer isso. A saída do console é:

    console:

    bad batch run, dude utility.js (line 109) response.status:404

    Agora, vamos alterar um pouco as coisas e usar um manipulador de falhas genérico reutilizável, mas também um curry em tempo de execução com o contexto de chamada conhecido em tempo de código e as informações de tempo de execução disponíveis no 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); }; } 

    console:

    Object { user_msg="bad batch run, dude. you were calling :Run ACL now"} utility.js (line 117) response.status:404 utility.js (line 118)

    Mais geralmente, dado o uso de callback generalizado em JS, o curry parece ser uma ferramenta 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