[a,b].reduce(f,x) code to [a,b].reduce(f) using transducer /CPS based functional references?

試著忘記壹切 提交于 2019-12-02 09:49:14

问题


In my previous Quesion:

Extracting data from a function chain without arrays

@Aadit M Shah gave me astonishing solution as follows:

https://stackoverflow.com/a/51420884/6440264

Given an expression like A(a)(b)(f) where f is a function, it's impossible to know whether f is supposed to be added to the list or whether it's the reducing function. Hence, I'm going to describe how to write expressions like A(a)(b)(f, x) which is equivalent to [a, b].reduce(f, x). This allows us to distinguish when the list ends depending upon how many arguments you provide:

const L = g => function (x, a) {
    switch (arguments.length) {
    case 1: return L(k => g((f, a) => k(f, f(a, x))));
    case 2: return g((f, a) => a)(x, a);
    }
};

const A = L(x => x);

const xs = A(1)(2)(3)(4)(5);

console.log(xs((x, y) => x + y, 0));        // 15
console.log(xs((x, y) => x * y, 1));        // 120
console.log(xs((a, x) => a.concat(x), [])); // [1,2,3,4,5]

It works due to continuations. Every time we add a new element, we accumulate a CPS function. Each CPS function calls the previous CPS function, thereby creating a CPS function chain. When we give this CPS function chain a base function, it unrolls the chain and allows us to reduce it. It's the same idea behind transducers and lenses.

There are 2 issues remained for me.

  1. To distinguish reducing function, I consider some custom Typing mechanism using reflection, but in order to focus on this issue, so far I would like to simply apply

    const isReducer = f => (typeof f === 'function');

  2. Requirement to provide an initial value has a limit to fold/reduce, for instance, it's impossible to provide an initial value for binary operations to the reduce such as

    const head = (a, b) => a; const tail = (a, b) => b;

(unless you provide the first/last value manually that makes no sense to run the code) In theory, every binary operations has a identity value, but something is impossible to provide as it is. The only way is to abstract as an identity.

Having said that, I can not refactor the provided code to single arguments and by a reducer type of the function, and the default value as the initial value of the sequence.

Can you provide the refactored code? Also any information of transducer/ CPS for this example is appreciated.


回答1:


When you don't provide an initial value, you lose a lot of power. For example, you won't be able to convert the list into an array. This is because the return type has to match the type of the elements of the list. Consider:

foldl :: (b -> a -> b) -> b -> [a] -> b
foldl f a []     = a
foldl f a (x:xs) = foldl f (f a x) xs

foldl1 :: (a -> a -> a) -> [a] -> a
foldl1 f (x:xs) = foldl f x xs

As you can see, the return type of foldl can be any type b which is independent of the type a. However, the return type of foldl1 is forced to be a. Hence, if you have a list A(1)(2)(3)(4)(5) then when you fold this list the result would necessarily have to be a number.

You could get away with it by doing something like A(1)(2)(3)(4)(5)(concat2) where const concat2 = (x, y) => [].concat(x, y) because JavaScript is not a strongly typed language. However, it's not consistent because A([1,2,3])([4,5,6])([7,8,9])(concat2) evaluates to [1,2,3,4,5,6,7,8,9] instead of [[1,2,3],[4,5,6],[7,8,9]]. I don't see any way to convert the second list into an array while preserving its structure if you don't have an initial value.


Nevertheless, if you still want to do that then there's very little you'd have to change. Notice that foldl1 just delegates work to foldl. Hence, we just have to keep the first element of the list separate from the rest and use it as the initial value of the accumulator:

const L = g => a => x => typeof x !== "function" ?
    L((f, a) => f(g(f, a), x))(a) :
    g(x, a);

const A = L((f, a) => a);

const xs = A(1)(2)(3)(4)(5);

console.log(xs((x, y) => x + y)); // 15
console.log(xs((x, y) => x * y)); // 120

Finally, if you really want to learn about functional programming and continuations then I suggest that you read SICP (Structure and Interpretation of Computer Programs) or HtDP (How to Design Programs). HtDP is generally considered more beginner friendly. However, I strongly recommend reading SICP.



来源:https://stackoverflow.com/questions/51427797/a-b-reducef-x-code-to-a-b-reducef-using-transducer-cps-based-functional

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!