Higher order folder function using callbacks
I want to write a function that provided a list of functions that will add up through this list, gradually passing the result from each closure to the next.
The functional signature of the functions in the list would be like (pseudocode):
typealias DoneClosure = (Dictionary) -> Void
typealias Middleware = (Dictionary, DoneClosure) -> Void
I would have a list of type Middleware
, and I want to shrink from left to right by folding the list and passing the result of each closure to the next closure.
let middleware1 = { acc, done in
// do something with acc, modify it
done(acc)
}
Each function will look something like this: they modify the drive in some way, and then pass the result of the executed or next function.
What I'm looking for is a recursive function that can be folded through a list using callbacks so that it can be async processed. Can anyone help me? (Language doesn't matter, but JS or Swift is preferred).
source to share
// does not handle empty middlewareList
const makeFunctionChain = function (middlewareList, initalDic) {
const length = middlewareList.length;
let i = 0;
let middleware;
const next = function (localAccumulator) {
middleware = middlewareList[i];
i += 1;
if (i === length) {
// there is no next
// pass a do-nothing function
middleware(localAccumulator, function() {});
} else {
middleware(localAccumulator, next);
}
};
next(initalDic);
};
// usage example
const middlewareAddOnions = function (food, next) {
// simple middleware
food["onions"] = 3;
next(food);
};
const middlewareAddWater = function (food, next) {
// here we use a new accumulator
const newFood = Object.assign({}, food, {withWater: true});
next(newFood);
};
const middlewareCook = function (food, next) {
// next can also be called asynchronously.
// here we use setTimeout
const timeToCook = 1500;
setTimeout(function () {
food.cooked = true;
next(food);
}, timeToCook);
};
const middlewareServe = function (food, next) {
// here we read the data
// does not use next
if (food.cooked) {
console.log(`Time to eat: ${JSON.stringify(food)}`);
} else {
console.log(`Something went wrong: ${JSON.stringify(food)}`);
}
};
// time to try it out
const food = {
eggs: 4,
potatoes: 12,
// ...
};
makeFunctionChain([
middlewareAddOnions,
middlewareAddWater,
middlewareCook,
middlewareServe
], food);
As noted in the comments, it is also possible to use Promises to get a similar result.
source to share
I was able to figure it out using recursion (in Swift)
typealias Next = (JSON) -> ()
typealias JSON = [String: Any]
typealias Middleware = (JSON, Next) -> ()
func process(middlewareList: [Middleware], value: JSON, done: @escaping Next) {
guard !middlewareList.isEmpty else {
done(value)
return
}
let middleware = middlewareList.first!
let slice = Array(middlewareList[1..<middlewareList.count])
middleware(value) { json in
process(middlewareList: slice, value: json, done: done)
}
}
let hook1: Middleware = { json, done in
print("hook 1")
done(json)
}
let hook2: Middleware = { json, done in
print("hook 2")
var copy = json
copy["hi"] = "hi"
done(copy)
}
process(middlewareList: [hook1, hook2], value: [:], done: { json in
print(json)
})
source to share