Pointfree使用spreaded参数组成函数

时间:2017-10-10 10:01:35

标签: javascript ramda.js pointfree

我试图找出是否存在一种用于编写无点组合函数的模式,当参数应该在curry composing函数中传播时 即(与Ramda):

add_1_and_multiply = (add, mul) => R.compose(R.multiply(mul), R.add(1))(add)
add_1_and_multiply(3, 5)  // 20

如何以无点样式编写add_1_and_multiply

1 个答案:

答案 0 :(得分:2)

我不确定你是否可以轻松地将无点风格和非一元性结合起来。 首先考虑结果和组合函数的类型:

// Compose:         (B  ->  C) -> (A  ->  B) ->  A  ->  C
const compose = f => g => x => f(g(x))
// Add:              A  ->  A  ->  A
const add = x => y => x + y
// Mul:              A  ->  A  ->  A
const mul = x => y => x * y

// Add1:             A  ->  A
const add1 = add(1)

// Add1AndMul:       A  ->         A  ->  A
//   because:
//     Add1:         A  ->  A
//     Mul:                 A  ->  A  ->  A
const add_1_and_mul = compose(mul)(add1)

// Mul4:             A  ->  A
const mul_4 = add_1_and_mul(3)
const result = mul_4(5) //> 20

Ramda有uncurryN所以你可以将它包裹在compose周围并摆脱所产生函数的干扰。

const add_1_and_multiply = R.uncurryN(2, R.compose(R.multiply, R.add(1)))
let result2 = add_1_and_multiply(3, 5) //> 20

将另一个功能添加到"链"你需要用以前的功能来组合它。

// Add1AndMul:          A -> A -> A
const add1_mul = compose(mul)(add1)

这是我们想要的签名。

//                      1         2         3
// Add1AndMulAndAdd:    A ->      A ->      A -> A
//  which is:           |         |         |
//      Add1:           A -> A    |         |
//      Mul:                 A -> A -> A    |
//      Add:                           A -> A -> A

所以我们不得不传递那些A 2 和A 3 而没有任何"点"。 让我们尝试简单的组合并分析它:

let add1_mul_add = compose(add)(add1_mul)

记住撰写:(E -> F) -> (D -> E) -> D -> F! 分步进行分析:

  1. 我们提供的是add函数签名,而不是(E -> F)

     (E -> F     )
     (A -> A -> A)
    

    我们得出结论

     E = A
     F = A -> A
    
  2. 我们对(D -> E)add1_mul

    执行相同的操作
     (D -> E     )
     (A -> A -> A)
    

    我们得出结论

     D = A
     E = A -> A
    
  3. 但我们已经看到了矛盾! 步骤2中的结论与步骤1中的结论相矛盾: E不能同时为AA -> A

    因此,我们无法撰写addadd1_mul,我们的add1_mul_add会抛出错误。

    让我们尝试解决这个问题并修复它违反了我们对无点风格的承诺。

    const add1_mul_add = x => compose(add)(add1_mul(x))
    

    我打算打破一些规则并将签名与代码混合以说明我的观点:

    x -> (A -> A -> A) -> (x -> A -> A) -> A -> A -> A
                           ||
                           \/
    x -> (A -> A -> A) -> (A -> A) -> A -> A -> A
         (E -> F     ) -> (D -> E) -> D -> F
    

    所以我们得到了正确的撰写签名!如何摆脱x变量回到无点? 我们可以尝试寻找明显的模式,例如......我们的功能组合!

    f(g(x)) => compose(f)(g)
    

    我们在新的add1_mul_add -

    中找到了这种模式
    f = compose(add)
    g = add1_mul
    f(g(x)) = compose(add)(add1_mul(x))
    

    我们将它缩减为无点,我们得到了新的add1_mul_add函数:

    const add1_mul_add = compose(compose(add))(add1_mul)
    

    但是嘿 - 我们可以减少它!

    const add1_mul_add = compose(compose)(compose)(add)(add1_mul)
    

    我们在The Owl的名字中找到了haskell中已存在的东西。

    我们可以在Javascript中将其定义为:

    const owl = compose(compose)(compose)
    

    但是现在,对于链中的每个新函数,您必须创建更高阶的owl运算符。

    const owl2 = compose(compose)(owl)
    const add1_mul_add_mul = owl2(mul)(add1_mul_add)
    
    const owl3 = compose(compose)(owl2)
    const add1_mul_add_mul_add = owl3(add)(add1_mul_add_mul)
    

    所以我真的建议让你的功能以无点的方式保持一致。或者使用其他结构,如列表:

    const actions = [ add, mul, add, mul ]
    const values  = [ 1,   2,   3,   4   ]
    const add_mul_add_mul = (...values) => zip(actions, values).reduce((acc, [action, value]) => action(acc, value), 0)