如何调和Javascript与咖喱和功能组成 [英] How to reconcile Javascript with currying and function composition

本文介绍了如何调和Javascript与咖喱和功能组成的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我喜欢currying,但是有一些原因促使一大堆JavaScript开发者拒绝这种技术:


  1. 审美关注典型curry pattern: f(x)(y)(z)

  2. 关注由于函数调用次数增加导致的性能损失

  3. 由于存在多个嵌套的匿名函数而担心调试问题。

  4. 关注无点式样的可读性(与构图有关的曲调)
  5. >

有没有一种方法可以缓解这些担忧,以便我的同事不讨厌我?

解决方案


注意: @ftor回答了他/她自己的问题。这是该答案的直接伴侣。


您已经是天才了



我想你可能已经重新想象了部分函数 - 至少部分是!

  const partial =(f,... xs)=> (... ys)=> f(... xs,... ys); 

,它是配对的, partialRight

  const partialRight =(f,... xs)=> (... ys)=> f(... ys,... xs); 

partial 需要一个函数,一些args xs ),并且 always 返回一个需要更多参数( ys )的函数,然后将 f 应用于(... xs,... ys)






最初的评论

这个问题的背景是咖啡和咖啡的组合可以在编码员的大量用户基础上发挥出色。我的评论将会出现在同一个上下文中


  • 仅仅因为一个函数可能返回一个函数并不意味着它是 curry - 在 _ 上添加以表示一个函数正在等待更多的参数是令人困惑的。回想一下,currying(或部分函数应用程序)抽象arity,所以我们永远不知道函数调用何时会导致计算值或其他函数的值等待调用。


  • 咖喱翻转参数;如果我们要为 reduce

  • 创建一个封装器,那么这会对您的编码器造成一些严重的wtf时刻。

    code> reduceRight
    wrapper应该是一致的 - 例如,你的 foldl 使用 f(acc,x,i)但您的 foldr 使用 f(x,acc,i) - 这会在不熟悉这些选择的同事之间造成很大的痛苦




下一节,我将用 partial 替换 composable ,删除 _ -suffixes,并修复 foldr wrapper






< data-lang =jsdata-hide =false数据控制台可组合函数 =truedata-babel =false>

const partial =( f,... xs)=> (... ys)=> f(... xs,... ys); const partialRight =(f,... xs)=> (... ys)=> f(... ys,... xs); const comp =(f,g)=> x => f(g(x)); const foldl =(f,acc,xs)=> xs.reduce(f,acc); const drop =(xs,n)=> xs.slice(n); const add =(x,y)=> x + y; const sum = partial(foldl,add,0); const dropAndSum = comp(sum,partialRight(drop,1)); console.log(dropAndSum([1,2,3,4])// 9 );






编程解决方案

const partial =(f, ... xs)=> (... ys)=> f(... xs,... ys); //恢复一致的interfaceconst foldr =(f,acc,xs)=> xs.reduceRight(f,acc); const comp =(f,g)=> x => f(g(x)); //将其添加为laterconst flip = f => (x,y)=> f(y,x); const I = x => x; const inc = x => x + 1; const compn = partial(foldr,flip(comp),I); const inc3 = compn([inc,inc,inc]); console.log(inc3(0)// 3);







更严重任务


const partial =(f,... xs )=> (... ys)=> f(... xs,... ys); const filter =(f,xs)=> xs.filter(f); const comp2 =(f,g,x,y)=> f(g(x,y)); const len = xs => xs.length; const odd = x => x%2 === 1; const countWhere = f =>部分(comp2,len,filter,f); const countWhereOdd = countWhere(odd); console.log(countWhereOdd([1,2,3,4,5])// 3);



部分实际上可以根据需要多次应用

< pre class =snippet-code-js lang-js prettyprint-override> const partial =(f,... xs)=> (... ys)=> f(... xs,... ys)const p =(a,b,c,d,e,f)=> a + b + c + d + e + flet f = partial(p,1,2)let g = partial(f,3,4)let h = partial(g,5,6)console.log(p ,2,3,4,5,6))// 21console.log(f(3,4,5,6))// 21console.log(g(5,6))// 21console.log(h( ))// 21



这使它成为工作中不可或缺的工具使用可变参数函数也是如此数据库控制台数据库控制台数据库控制台数据库数据库数据库数据库数据库数据库数据库数据库babel =false>

const partial =(f,... xs )=> (... ys)=> f(... xs,... ys)const add =(x,y)=> x + yconst p =(... xs)=> xs.reduce(add,0)让f = partial(p,1,1,1,1)let g = partial(f,2,2,2,2)let h = partial(g,3,3,3 ,3)console.log(h(4,4,4,4))// 1 + 1 + 1 + 1 + // 2 + 2 + 2 + 2 + // 3 + 3 + 3 + 3 + // 4 + 4 + 4 + 4 => 40



最后,演示 partialRight



const partial =(f,.. .xs)=> (... ys)=> f(... xs,... ys); const partialRight =(f,... xs)=> (... ys)=> f(... ys,... xs); const p =(... xs)=> console.log(... xs)const f = partialRight(p,7,8,9); const g = partial(f,1,2,3); const h = partial(g,4,5,6) ; p(1,2,3,4,5,6,7,8,9)// 1 2 3 4 5 6 7 8 9f(1,2,3,4,5,6)// 1 2 3 4 5 6 7 8 9g(4,5,6)// 1 2 3 4 5 6 7 8 9h()// 1 2 3 4 5 6 7 8 9






b

好的,所以 partial 几乎是替代可组合的的下降,一些额外的角落案件。让我们来看看这是如何与你最初的名单冲突的。


  1. 美学问题:避免 f(x)(y)( z)

  2. 性能:不确定,但我怀疑性能大致相同

  3. 调试仍然是个问题,因为 partial 创建新函数

  4. 可读性:实际上,我认为可读性非常好。 部分足够灵活,可以在很多情况下删除点数

我同意你的看法没有完全咖喱功能的替代品。我个人发现,一旦我停止判断语法的丑陋,就很容易采用这种新风格 - 它只是不同而人们不喜欢不同。


I love currying but there are a couple of reasons why a lof of Javascript devs reject this technique:

  1. aesthetic concerns about the typical curry pattern: f(x) (y) (z)
  2. concerns about performance penalties due to the increased number of function calls
  3. concerns about debugging issues because of the many nested anonymous functions
  4. concerns about readability of point-free style (currying in connection with composition)

Is there an approach that can mitigate these concerns so that my coworkers don't hate me?

解决方案

Note: @ftor answered his/her own question. This is a direct companion to that answer.

You're already a genius

I think you might've re-imagined the partial function – at least, in part!

const partial = (f, ...xs) => (...ys) => f(...xs, ...ys);

and it's counter-part, partialRight

const partialRight = (f, ...xs) => (...ys) => f(...ys, ...xs);

partial takes a function, some args (xs), and always returns a function that takes some more args (ys), then applies f to (...xs, ...ys)


Initial remarks

The context of this question is set in how currying and composition can play nice with a large user base of coders. My remarks will be in the same context

  • just because a function may return a function does not mean that it is curried – tacking on a _ to signify that a function is waiting for more args is confusing. Recall that currying (or partial function application) abstracts arity, so we never know when a function call will result in the value of a computation or another function waiting to be called.

  • curry should not flip arguments; that is going to cause some serious wtf moments for your fellow coder

  • if we're going to create a wrapper for reduce, the reduceRight wrapper should be consistent – eg, your foldl uses f(acc, x, i) but your foldr uses f(x, acc, i) – this will cause a lot of pain amongst coworkers that aren't familiar with these choices

For the next section, I'm going to replace your composable with partial, remove _-suffixes, and fix the foldr wrapper


Composable functions

const partial = (f, ...xs) => (...ys) => f(...xs, ...ys);

const partialRight = (f, ...xs) => (...ys) => f(...ys, ...xs);

const comp = (f, g) => x => f(g(x));

const foldl = (f, acc, xs) => xs.reduce(f, acc);

const drop = (xs, n) => xs.slice(n);

const add = (x, y) => x + y;

const sum = partial(foldl, add, 0);

const dropAndSum = comp(sum, partialRight(drop, 1));

console.log(
  dropAndSum([1,2,3,4]) // 9
);


Programmatic solution

const partial = (f, ...xs) => (...ys) => f(...xs, ...ys);

// restore consistent interface
const foldr = (f, acc, xs) => xs.reduceRight(f, acc);

const comp = (f,g) => x => f(g(x));

// added this for later
const flip = f => (x,y) => f(y,x);

const I = x => x;

const inc = x => x + 1;

const compn = partial(foldr, flip(comp), I);

const inc3 = compn([inc, inc, inc]);

console.log(
  inc3(0) // 3
);


A more serious task

const partial = (f, ...xs) => (...ys) => f(...xs, ...ys);

const filter = (f, xs) => xs.filter(f);

const comp2 = (f, g, x, y) => f(g(x, y));

const len = xs => xs.length;

const odd = x => x % 2 === 1;

const countWhere = f => partial(comp2, len, filter, f);

const countWhereOdd = countWhere(odd);

console.log(
   countWhereOdd([1,2,3,4,5]) // 3
);


Partial power !

partial can actually be applied as many times as needed

const partial = (f, ...xs) => (...ys) => f(...xs, ...ys)

const p = (a,b,c,d,e,f) => a + b + c + d + e + f

let f = partial(p,1,2)
let g = partial(f,3,4)
let h = partial(g,5,6)

console.log(p(1,2,3,4,5,6)) // 21
console.log(f(3,4,5,6))     // 21
console.log(g(5,6))         // 21
console.log(h())            // 21

This makes it an indispensable tool for working with variadic functions, too

const partial = (f, ...xs) => (...ys) => f(...xs, ...ys)

const add = (x,y) => x + y

const p = (...xs) => xs.reduce(add, 0)

let f = partial(p,1,1,1,1)
let g = partial(f,2,2,2,2)
let h = partial(g,3,3,3,3)

console.log(h(4,4,4,4))
// 1 + 1 + 1 + 1 +
// 2 + 2 + 2 + 2 +
// 3 + 3 + 3 + 3 +
// 4 + 4 + 4 + 4 => 40

Lastly, a demonstration of partialRight

const partial = (f, ...xs) => (...ys) => f(...xs, ...ys);

const partialRight = (f, ...xs) => (...ys) => f(...ys, ...xs);

const p = (...xs) => console.log(...xs)

const f = partialRight(p, 7, 8, 9);
const g = partial(f, 1, 2, 3);
const h = partial(g, 4, 5, 6);

p(1, 2, 3, 4, 5, 6, 7, 8, 9) // 1 2 3 4 5 6 7 8 9
f(1, 2, 3, 4, 5, 6)          // 1 2 3 4 5 6 7 8 9
g(4, 5, 6)                   // 1 2 3 4 5 6 7 8 9
h()                          // 1 2 3 4 5 6 7 8 9


Summary

OK, so partial is pretty much a drop in replacement for composable but also tackles some additional corner cases. Let's see how this bangs up against your initial list

  1. aesthetic concerns: avoids f (x) (y) (z)
  2. performance: unsure, but i suspect performance is about the same
  3. debugging: still an issue because partial creates new functions
  4. readability: i think readability here is pretty good, actually. partial is flexible enough to remove points in many cases

I agree with you that there's no replacement for fully curried functions. I personally found it easy to adopt the new style once I stopped being judgmental about the "ugliness" of the syntax – it's just different and people don't like different.

这篇关于如何调和Javascript与咖喱和功能组成的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆