Converting function with logic and ui into higher order functions - javascript

I'm trying to improve my JavaScript skills. I'm learning composability and functional patterns and I'm totally lost.
I have two functions: one mapping an array and the other called from within the previous function to generate the markup.
const names = ['peter', 'paul', 'patrice']
const namesMarkup = name => {
return `<p>${name}</p>`
}
const showNames = listOfNames => {
return listOfNames.map(el => {
return namesMarkup(el)
})
}
showNames(names)
I have been reading about HOF, which technically are functions that take a function as an argument and/or return a function.
How could I compose these functions to have a HOF?
I went through the basic examples like
const square = num => num * num
const plus10 = (num, callback) => {
return callback(num) + 10
}
console.log(addTwo(7, square))
but I cannot make my mind around the previous example and working with lists.
I will appreciate help since the more I research the more confused I get.

Your mistake is to assume an array for showNames. Never do this. Always implement the simplest version of a function. In FP array is a computational effect. Don't implement such an effectful function as default:
const nameMarkup = name => {
return `<p>${name}</p>`;
}
const nameMarkup2 = name => {
return `<p>${name.toUpperCase()}!</p>`;
}
const showName = f => name => {
const r = f(name);
/* do something useful with r */
return r;
}
const names = ['peter', 'paul', 'patrice']
console.log(
showName(nameMarkup) ("peter"));
// lift the HOF if you want to process a non-deterministic number of names:
console.log(
names.map(showName(nameMarkup2)));
Now swapping the markup just means to pass another function argument. Your showName is more general, because a HOF lets you pass part of the functionality.
If we drop the array requirement, your showNames doesn't do anything useful anymore. It still illustrates the underlying idea, though.

Related

Assign and Query Javascript Arrow Function for Metadata

The problem is rather simple. We need to imbue a function with a parameter, and then simply extract that parameter from the body of the function. I'll present the outline in typescript...
abstract class Puzzle {
abstract assign(param, fn): any;
abstract getAssignedValue(): any;
async test() {
const wrapped = this.assign(222, async () => {
return 555 + this.getAssignedValue();
});
console.log("Expecting", await wrapped(), "to be", 777);
}
}
Let's set the scene:
Assume strict mode, no arguments or callee. Should work reasonably well on the recent-ish version of v8.
The function passed to assign() must be an anonymous arrow function that doesn't take any parameters.
... and it's alsoasync. The assigned value could just be stored somewhere for the duration of the invocation, but because the function is async and can have awaits, you can't rely on the value keeping through multiple interleaved invocations.
this.getAssignedValue() takes no parameters, returning whatever we assigned with the assign() method.
Would be great to find a more elegant solution that those I've presented below.
Edit
Okay, we seem to have found a good solid solution inspired by zone.js. The same type of problem is solved there, and the solution is to override the meaning of some system-level primitives, such as SetTimeout and Promise. The only headache above was the async statement, which meant that the body of the function could be effectively reordered. Asyncs are ultimately triggered by promises, so you'll have to override your Promise with something that is context aware. It's quite involved, and because my use case is outside of browser or even node, I won't bore you with details. For most people hitting this kind of problem - just use zone.js.
Hacky Solution 2
class HackySolution2 extends Puzzle {
assign(param: any, fn: AnyFunction): AnyFunction {
const sub = Object(this);
sub["getAssignedValue"] = () => param;
return function () { return eval(fn.toString()); }.call(sub);
}
getAssignedValue() {
return undefined;
}
}
In this solution, I'm making an object that overrides the getAssignedValue() method, and re-evaluates the source code of the passed function, effectively changing the meaning of this. Still not quite production grade...
Edit.
Oops, this breaks closures.
I don't know typescript so possibly this isn't useful, but what about something like:
const build_assign_hooks = () => {
let assignment;
const get_value = () => assignment;
const assign = (param, fn) => {
assignment = param;
return fn;
}
return [assign, get_value];
};
class Puzzle {
constructor() {
const [assign, getAssignedValue] = build_assign_hooks();
this.assign = assign;
this.getAssignedValue = getAssignedValue;
}
async test() {
const wrapped = this.assign(222, async () => {
return 555 + this.getAssignedValue();
});
console.log("Expecting", await wrapped(), "to be", 777);
}
}
const puzzle = new Puzzle();
puzzle.test();
Hacky Solution 1
We actually have a working implementation. It's such a painful hack, but proves that this should be possible. Somehow. Maybe there's even a super simple solution that I'm missing just because I've been staring at this for too long.
class HackySolution extends Puzzle {
private readonly repo = {};
assign(param: any, fn) {
// code is a random field for repo. It must also be a valid JS fn name.
const code = 'd' + Math.floor(Math.random() * 1000001);
// Store the parameter with this code.
this.repo[code] = param;
// Create a function that has code as part of the name.
const name = `FN_TOKEN_${code}_END_TOKEN`;
const wrapper = new Function(`return function ${name}(){ return this(); }`)();
// Proceed with normal invocation, sending fn as the this argument.
return () => wrapper.call(fn);
}
getAssignedValue() {
// Comb through the stack trace for our FN_TOKEN / END_TOKEN pair, and extract the code.
const regex = /FN_TOKEN_(.*)_END_TOKEN/gm;
const code = regexGetFirstGroup(regex, new Error().stack);
return this.repo[code];
}
}
So the idea in our solution is to examine the stack trace of the new Error().stack, and wrap something we can extract as a token, which in turn we'll put into a repo. Hacky? Very hacky.
Notes
Testing shows that this is actually quite workable, but requires a more modern execution environment than we have - i.e. ES2017+.

What does this export mean? Is this an object executing two functions?

I came across this "higher order function". But I can't wrap my head around it.
Here's the snippet:
addUrlProps([options])(WrappedComponent)
Is this addUrlProps a function? What does it accept? It has two () beside it...
In React it's called a higher order function (HOC), but really it's just borrowing an old functional programming concept. What it really is, is an application of currying.
Simple Example
Let's say you want to create an add2 and add3 function. You'd do something like this:
// add2 = a => Number
const add2 = a => a + 2;
const sum2 = add2(5) // => 7
// add3 = a => Number
const add3 = a => a + 3;
const sum3 = add3(5) // => 8
The problem is this isn't really scalable. What if you needed to create add4, add5... add100? This is where currying comes into play;
// add = a => b => Number
const add = a => b => a + b;
// add(2)(5)
const add2 = add(2);
const sum2 = add2(5); // => 7
// add(3)(5)
const add3 = add(3);
const sum3 = add3(5); // => 8
...
// add(100)(5)
const add100 = add(100);
const sum100 = add100(5); // => 105
Theoretically you could create an infinite number of add functions to your taste.
Case Example
In your example addUrlProps([options])(WrappedComponent) is doing the exact same thing.
// addUrlProps([options])(WrappedComponent)
const foo = addUrlProps([options]);
const HOC = foo(WrappedComponent); // => A component
It's taking in [options] to create a function. Then that function takes a second argument called WrappedComponent.
addUrlProps is a function that returns a function. In react terms it is a HOC. HOC documentation The second () is invoking the function that is returned from the first invocation.
It is a shorthand for the following.
const func = addUrlProps();
func(WrappedComponent);
So instead of saving the result of addUrlProps() into a variable, it is invoked immediatley like so
export addUrlProps()(WrappedComponent);
Its returned value is the same component, wrapped in another component that is adding some extra functionality, or props.
It is worth noting that not all HOCs follow this curried pattern. A very popular library that uses this particular pattern is react-redux connect HOC. Don't be afraid to view the source code to figure out what is happening!

How do I run a function with arguments from an object?

I have a lot of functions like the following:
var runme = function(liked, disliked){
console.log(`i like ${liked} but I dislike ${disliked}`)
}
I have an object with I would like to use to fill in the arguments of the function
var obj = {liked: 'apple', disliked: 'pear'}
How can I run the function using the object to specify the arguments?
I tried using spread syntax:
runme(...obj)
But this produces:
TypeError: Found non-callable ##iterator
How can I run a function with parameters from an object?
I can't change the functions, as I am creating a wrapper that needs to be able to handle arbitrary functions.
Edit: I've edited the post to use 'liked' and 'disliked' instead of 'one' and 'two' as this better shows that ordering matters.
I can use any version of JavaScript, up to and including ES9.
There is no general-purpose way to accomplish this.
If you can guarantee the provenance of the source then you could use an AST operation to build your wrappers during a transpilation or load phase.
If you cannot, then you're particularly out of luck, because the parameter names may be mangled, making an object-key-to-parameter-name transformation impossible.
I can't change the functions, as I am creating a wrapper that needs to be able to handle arbitrary functions.
That's unfortunate, since making them accept a destructured parameter would be exactly what you need.
Unfortunately, the names of parameters are not available unless you parse the result of calling toString on the function, which is...fraught with peril. You basically need a full JavaScript parser (because parameter lists are complex these days, including possibly containing entire function definitions for default values) and minifiers and such may rename parameters (changing liked to _0, for instance).
You also can't count on the order of the properties in an object. (They do have an order, but not one that helps here...or almost anywhere else.)
You've said you need to handle functions whose parameters you don't know in advance, so my various ideas around wrapping functions with utilities that require passing in the names of the parameters won't work. (If anyone's curious, look at the revision list to see those.)
You can do this from toString if we can make several assumptions:
The function parameter lists are simple. They don't include destructuring or default values.
Comments are not used within the parameter lists.
Your minifier does not rename function parameters.
The functions are all traditional functions, methods, or arrow functions that do have () around the parameter list (so for instance, (x) => x * 2, not just x => x * 2).
You don't mind that it'll be fairly inefficient (parsing each time).
That's a lot of assumptions and I don't recommend it. But if you can rely on them:
// LOTS of assumptions here!
function run(f, obj) {
let params = /\(([\w\s,]*)\)/.exec(String(f));
if (!params) {
throw new Error("Couldn't parse function");
}
params = params[1].split(/\s*,\s*/).map(n => n.trim());
return f.apply(this, params.map(param => obj[param]));
}
run(runme, obj);
Live Example:
// Traditional function
const runme = function(liked, disliked){
console.log(`i like ${liked} but I hate ${disliked}`)
}
// Traditional function with newlines
const runme2 = function(
liked,
disliked
){
console.log(`i like ${liked} but I hate ${disliked}`)
}
// Arrow function
const runme3 = (liked, disliked) => {
console.log(`i like ${liked} but I hate ${disliked}`)
}
// Method
const {runme4} = {
runme4(liked, disliked) {
console.log(`i like ${liked} but I hate ${disliked}`)
}
};
const obj = {liked: 'apple', disliked: 'pear'}
function run(f, obj) {
let params = /\(([\w\s,]*)\)/.exec(String(f));
if (!params) {
throw new Error("Couldn't parse function");
}
params = params[1].split(/\s*,\s*/).map(n => n.trim());
return f.apply(this, params.map(param => obj[param]));
}
run(runme, obj);
run(runme2, obj);
run(runme3, obj);
run(runme4, obj);
That works because Function.prototype.toString is standardized now, and even in resource-constrained environments it's required to include the parameter list (but may well not include the rest of the function implementation).
Answer Re-written to factor in correct ordering.
So long as your object keys are named to match the paramaters, you can parse the functions like the following: You can see that no matter which order they are passed in, the output is correct;
var obj = {liked: 'apple', disliked: 'pear'}
var runme = function (liked, disliked) {
console.log(`i like ${liked} but I dislike ${disliked}`)
}
var translateFunc = function (func, args) {
let funcAsString = func.toString();
let argNames = funcAsString.slice(funcAsString.indexOf('(') + 1, funcAsString.indexOf(')')).match(/([^\s,]+)/g);
let parsedArgs = [];
for (let a of argNames) {
for (let k of Object.keys(args)) {
if (k == a) {
parsedArgs.push(args[a]);
}
}
}
eval(func(...parsedArgs));
}
translateFunc(runme, obj);
obj = {disliked: 'pear', liked: 'apple'}
translateFunc(runme, obj);
A bit late, but here's another try. I think it works, but minification of the code will change your function arguments but not the object properties, so you'll need to work around that.
var runme = function(liked, disliked){
console.log(`i like ${liked} but I dislike ${disliked}`)
}
var obj = {liked: 'apple', disliked: 'pear'}
const wrap = runme => {
const regex = new RegExp(/\(([^\)]*)\)/)
const args = regex.exec(runme.toString())[1].split(',').map(s => s.trim())
return obj => {
const result = args.map(a => obj[a])
runme(...result)
}
}
const wrappedRunme = wrap(runme);
console.log(wrappedRunme(obj))
I got the same error and just in case this was the issue, here's what resolved it for me...
Instead of:
runme(...obj)
I needed to spread the object into a new object:
runme({ ...obj })
You can do that in following steps:
First convert the function to a string
Use RegExp to get the arguments of the function.
Use split() to convert the argument string to array of arguments.
Then use reduce() on that array and create a new ordered array having values of given object.
var runme = function(liked, disliked){
console.log(`i like ${liked} but I dislike ${disliked}`)
}
function wrapper(obj){
let args = runme.toString().match(/\(.+\)/)[0]
args = args.slice(1,-1);
args = args.split(',').map(x => x.trim());
let ordered = args.reduce((ac,ar) => [...ac,obj[ar]],[]);
runme(...ordered);
}
wrapper({liked:"liked", disliked:"disliked"})
wrapper({ disliked:"disliked",liked:"liked"})

How to nest Reselect selectors?

I have a selector that returns an array. The elements in the array themselves have derived data. I essentially need a recursive memoization selector that returns a derived array composed of derived elements.
my current attempt is:
export const selectEntitesWithAssetBuffers = createSelector(
[selectSceneEntities, getAssets],
(entities, loadedAssets) => {
return entities.map((entity) => {
entity.buffers = entity.assets.map((assetName) => {
return loadedAssets[assetName].arrayBuffer;
})
return entity;
})
}
)
My concerns here are anytime entities or loadedAssets change this will recompute the entire list. What I'm expecting to setup is something like a selectEntityWithBuffer that would get passed to the entities.map. Ideally, I want this to only recompute when an entity.assets array changes.
Reselect allows you to provide custom equality definitions to your selectors.
import { defaultMemoize, createSelectorCreator } from 'reselect'
const compareByAssets = (a, b) => {
return a.every((element, index) => {
return element.assets === b[index].assets
});
};
const createAssetsComparatorSelector = createSelectorCreator(
defaultMemoize,
compareByAssets
);
const selectSceneEntitiesByAssetsComparator = createAssetsComparatorSelector((state) => {
//however you normally get entities for the pre-existing selectors
});
Now you can use this new selectSceneEntitiesByAssetsComparator in place of the previous selectSceneEntities in the above code you provided and it will only re-run when the equality check in compareByAssets fails.
Feel free to further update that comparator function if a strict comparison of assets === assets doesn't suite your needs.
As a proof of concept, I'd try to provide loadedAssets object to the result function by bypassing reselect identity checks.
// Keep a private selector instance
let cachedSelector;
export const selectEntitesWithAssetBuffers = function(){
// loadedAssets should be recalculated on each call?
const loadedAssets = getAssets(arguments);
// create selector on first call
if(cachedSelector === undefined) {
cachedSelector = createSelector(
selectSceneEntities,
entities => {
return entities.map(entity => {
entity.buffers = entity.assets.map((assetName) => {
return loadedAssets[assetName].arrayBuffer;
})
return entity;
})
}
)
}
// Return selector result
return cachedSelector(arguments);
}
Getting deeper memoization than what you've got is kind of a tricky problem because Reselect doesn't really support passing arguments to selectors. If you're returning an array from your selector, and the input used to build that array has changed, it's sort of the intended behavior from Reselect that you will need to recompute. See the advice in the readme for dynamic arguments.

Trying to refactor this code to use only immutable structures

I'm a student of functional programming. I am still weaning myself off the old variable-mutation habits. But sometimes I get stuck. Ok, so here is the question--suppose we have the following closure
const bookShelf = () => {
let books = []
const listBooks = () => books
const addBook = (book) => {
books = books.concat(book)
return function removeBook() { books = books.filter( b => b !== book ) }
}
return {addBook,listBooks}
}
const { addBook, listBooks } = bookShelf()
const removeMobyDick = addBook('Moby Dick')
const removeWalden = addBook('Walden')
removeWalden()
console.log(listBooks()) // ["Moby Dick"]
Note that I have one object which is mutated: books.
My question is, how can I refactor this code so that books is immutable yet I achieve the same end-result. Feel free to use a functional library like Ramda if need be. My naive thought here is somehow use recursion to pass in a new value of books and then pass that version back. Seems a bit overreach so I thought to seek out help from someone more knowledgable in this arena.
Thanks for your insight!
Just leave book constant in your bookshelf. That will require creating a new bookshelf every time of course, so the easiest approach is to make books be a parameter of the function:
function bookShelf(books) {
return {
listBooks() { return books },
addBook(book) { return bookShelf(books.concat([book])); }
}
}
const empty = bookShelf([]);
const shelfWithMobyDick = empty.addBook('Moby Dick');
const shelfWithMobyDickAndWalden = shelfWithMobyDick.addBook('Walden');
console.log(shelfWithMobyDick.listBooks());
As you can see, there's no need for a removeBook function - you just use the old value that had not yet included the book.
If you want to be able to remove the book you just added from an arbitrary bookshelf, you can also return both the new bookshelf and a remover function:
…,
addBook(book) {
return {
bookShelf: bookShelf(books.concat([book]));
removeThis(shelf) { return bookShelf(shelf.listBooks().filter(b => b !== book)); }
};
}
to be used as
const empty = bookShelf([]);
const {bookShelf: shelfWithMobyDick, removeThis: removeMobyDick} = empty.addBook('Moby Dick');
const {bookShelf: shelfWithMobyDickAndWalden, removeThis: removeWalden} = shelfWithMobyDick.addBook('Walden');
const shelfWithWalden = removeMobyDick(shelfWithMobyDickAndWalden);
console.log(shelfWithWalden.listBooks());
The bookshelf type doesn’t really seem to be accomplishing anything here, so let’s just make it a list (array).
let bookshelf = [];
Now it looks like you want a way to produce a list with a new item and a way of removing that item from the list. A little weird, but you can do that by returning both in a tuple (array):
const remove = (list, value) =>
list.filter(x => x !== value);
const addRemovable = (list, value) =>
[[...list, value], list => remove(list, value)];
let bookshelf = [];
let removeMobyDick;
let removeWalden;
[bookshelf, removeMobyDick] = addRemovable(bookshelf, 'Moby Dick');
[bookshelf, removeWalden] = addRemovable(bookshelf, 'Walden');
bookshelf = removeWalden(bookshelf);
console.log(bookshelf);
This doesn’t look nice and you probably wouldn’t want to write something like it, but it does achieve the same thing as your original.

Categories

Resources