Defining chained string transform functions in Javascript - javascript

Chaining Methods, also known as Cascading, refers to repeatedly calling one method after another on an object, in one continuous line of code.
Writing code like this:
str.replace("k", "R").toUpperCase().substr(0,4);
is not just pleasurable and convenient but also succinct and intelligible. It allows us to read code like a sentence, flowing gracefully across the page. It also frees us from the monotonous, blocky structures we usually construct.
This blog talks about how to do it generally, however considering extending the String prototype will be good enough for my case, I don't know whether it is an overkill or not.
Basically I don't know enough Javascript to make the judgement call of,
whether go with the above general way,
or just extend the String prototype, knowing that it is generally a bad practice
or maybe using function wrapper (or maybe it is totally irrelevant)
I also know the following is another option, but it's kind of rigid, I.e., translated into my case, it means the string transform can only be
replace("k", "R") then followed by toUpperCase() then followed by substr(0,4).
// https://medium.com/javascript-scene/javascript-factory-functions-with-es6-4d224591a8b1
console.log('Begin');
const withConstructor = constructor => o => {
const proto = Object.assign({},
Object.getPrototypeOf(o),
{ constructor }
);
return Object.assign(Object.create(proto), o);
};
const pipe = (...fns) => x => fns.reduce((y, f) => f(y), x);
// or `import pipe from 'lodash/fp/flow';`
// Set up some functional mixins
const withFlying = o => {
let isFlying = false;
return {
...o,
fly () {
isFlying = true;
return this;
},
land () {
isFlying = false;
return this;
},
isFlying: () => isFlying
}
};
const withBattery = ({ capacity }) => o => {
let percentCharged = 100;
return {
...o,
draw (percent) {
const remaining = percentCharged - percent;
percentCharged = remaining > 0 ? remaining : 0;
return this;
},
getCharge: () => percentCharged,
get capacity () {
return capacity
}
};
};
const createDrone = ({ capacity = '3000mAh' }) => pipe(
withFlying,
withBattery({ capacity }),
withConstructor(createDrone)
)({});
const myDrone = createDrone({ capacity: '5500mAh' });
console.log(`
can fly: ${ myDrone.fly().isFlying() === true }
can land: ${ myDrone.land().isFlying() === false }
battery capacity: ${ myDrone.capacity }
battery status: ${ myDrone.draw(50).getCharge() }%
battery drained: ${ myDrone.draw(75).getCharge() }%
`);
console.log(`
constructor linked: ${ myDrone.constructor === createDrone }
`);
console.log('End');
To make it simple, I need to define my own functions like
str.transform1("k", "R").transform2().transform3(0,4);
that do just as
str.replace("k", "R").toUpperCase().substr(0,4);
In reality each function will be a set of complicated replace() function call, but let's make it simple for this question.
What best should I do?

Related

Can't access function attribute inside decoration function

In the following code I am trying to add decorators to a function. For certain reasons I would like to display the function attribute "name". However, I have no access to it as soon as I am in the individual functions. Also, I'm not sure why the functions are called from the bottom up. What are the reasons for all of the points mentioned and how can I avoid them?
let rectangleArea = (length, width) => {
return length * width;
}
const countParams = (fn) => {
return (...params) => {
console.log('countParams', fn.name)
if (params.length !== fn.length) {
throw new Error(`Incorrect number of parameters for ${fn.name}!`);
}
return fn(...params);
}
}
const requireIntegers = (fn) => {
return (...params) => {
console.log('requireIntegers', fn.name)
params.forEach(param => {
if (!Number.isInteger(param)) {
throw new TypeError(`Params must be integers at ${fn.name}!`); //Can't access fn.name
}
});
return fn(...params);
}
}
//Why running from bottom to top?
rectangleArea = countParams(rectangleArea);
rectangleArea = requireIntegers(rectangleArea);
console.log(rectangleArea(20, 30, "hey"));
The first time you make a decorated function for a given function, that returned function does not have a name -- it is anonymous. So when you then pass that decorated function to be decorated again, fn will be that anonymous function.
To solve this, assign the name of the fn function also to the returned decorated function. That way the name will stick even when you decorate that function again, and again...
Here is a helper function that will assign the name property to a given function:
const setName = (deco, value) => {
Object.defineProperty(deco, "name", {value, writable: false});
return deco;
}
let rectangleArea = (length, width) => {
return length * width;
}
const countParams = (fn) => {
return setName((...params) => {
console.log('countParams', fn.name)
if (params.length !== fn.length) {
throw new Error(`Incorrect number of parameters for ${fn.name}!`);
}
return fn(...params);
}, fn.name);
}
const requireIntegers = (fn) => {
return setName((...params) => {
console.log('requireIntegers', fn.name)
params.forEach(param => {
if (!Number.isInteger(param)) {
throw new TypeError(`Params must be integers at ${fn.name}!`); //Can't access fn.name
}
});
return fn(...params);
}, fn.name);
}
rectangleArea = countParams(rectangleArea);
rectangleArea = requireIntegers(rectangleArea);
console.log(rectangleArea(20, 30, "hey"));
Why the functions are called from the bottom up.
Because in your decorator the last step is to call fn.
That fn might be an already decorated function, and so it is normal that earlier decorations of the function run later.
It is like wrapping a birthday present several times, each time with a different color of wrapping paper. When your friend unpacks it, they will get to see the colors of wrapping paper in the reverse order from the order in which you had applied them.
So you want to do something additional with your decorators? They need some common behavior? We've shown we know how to do that already: with decorators. Your decorators need decorators of their own!
Here I write a decorator-decorator keep which takes a decorator function and returns a new decorator function which keeps the name and length properties of the function passed to it. (Say that five times fast!)
It uses the same technique as the answer from trincot, but is less intrusive, as you can simply wrap the decorator functions just as you would the underlying ones. Here I do that at definition time, since we never really want these decorators without this behavior, but you can do it as you like.
let rectangleArea = (length, width) => {
return length * width;
}
const keep = (decorator) => (fn) =>
Object .defineProperties (decorator (fn), {
name: {value: fn .name, writable: false},
length: {value: fn .length, writable: false}
})
const countParams = keep ((fn) => {
return (...params) => {
console.log('countParams', fn.name)
if (params.length !== fn.length) {
throw new Error(`Incorrect number of parameters for ${fn.name}!`);
}
return fn(...params);
}
})
const requireIntegers = keep ((fn) => {
return (...params) => {
console.log('requireIntegers', fn.name)
params.forEach(param => {
if (!Number.isInteger(param)) {
throw new TypeError(`Params must be integers at ${fn.name}!`); //Can't access fn.name
}
});
return fn(...params);
}
})
//Why running from bottom to top? -- answered by #balastrong
rectangleArea = countParams(rectangleArea);
rectangleArea = requireIntegers(rectangleArea);
console.log(rectangleArea(20, 30));
console.log(rectangleArea(20, 30, "hey"));
.as-console-wrapper {max-height: 100% !important; top: 0}
The name keep was originally keepName before I realized that I personally would also want the function to keep the arity intact. I couldn't come up with a clear useful name for this... which is a large warning sign to me. So there may be something still wrong with this design.
It's not from bottom to top, it's the order you set.
With your decorators, you basically just did this:
requireIntegers(countParams(rectangleArea(20, 30, "hey"))).
Which means first it executes requireIntegers by passing it as input everything else (countParams(rectangleArea(20, 30, "hey"))).
Then you see the console log and the error as params.forEach scans the params and finds 'hey' which is not a number.

How to override object get fallback

I'm trying to create an object that returns the property name for any property that was accessed. Is this possible to do cleanly in javascript / nodejs? This is what I would like to accomplish:
const mirror = {/*...*/};
console.log(mirror[1]);
// => 1
console.log(mirror.123);
// => '123'
console.log(mirror.randomPropertyHere);
// => 'randomPropertyHere'
I can overwrite a getter for a specific property, but I don't know how to do it generically. Also how can I differentiate between a number and a string?
My (not working) attempts
const mirror = {
get[1] () {
console.log('number');
return 1;
},
get['1'] () {
console.log('string');
return '1';
}
};
console.log(mirror[1]);
console.log(mirror['1']);
Very much appreciate your time and help!
With a proxy.
const mirror = new Proxy({}, {
get(_, prop) { return prop; }
});
console.log(mirror[1]);
// => 1
console.log(mirror['123']);
// => '123'
console.log(mirror.randomPropertyHere);
// => 'randomPropertyHere'
Also how can I differentiate between a number and a string?
Can't, object properties have to be strings or symbols.

Can this code be refactored to a more functional programming style using hof/compose/currying/partial application/monads?

I've just started learning about functional programming and I'm trying to put into practice what I've learned. I've got this code below and I just don't know where can I apply function composition, partial application in this function.
Any ideas how to refactor this using the functional techniques?
function compareCodes(validateFn, moreProcessingFn, doStuffOnCodeAFn, doStuffOnCodeBFn, doSomething1Fn, doSomething2Fn, codeA, codeB, param1, param2) {
let result = null;
if (validateFn(codeA, codeB)) {
const isCodeAValid = doStuffOnCodeAFn(codeA); // returns a boolean
const isCodeBValid = doStuffOnCodeBFn(codeB); // returns a boolean
const isItAMatch = moreProcessingFn(isCodeAValid, isCodeBValid, codeA, codeB); // returns a boolean
if (isItAMatch) {
result = doSomething1Fn (param1, param2);
} else {
result = doSomething2Fn (param1, param2);
}
}
return result;
}
The first step would be to get rid of all the helper variables. While the boolean intermediate variables ease understanding with their descriptive names, at least result is totally unnecessary.
function compareCodes(validateFn, moreProcessingFn, doStuffOnCodeAFn, doStuffOnCodeBFn, doSomething1Fn, doSomething2Fn, codeA, codeB, param1, param2) {
return validateFn(codeA, codeB)
? (moreProcessingFn(doStuffOnCodeAFn(codeA), doStuffOnCodeBFn(codeB), codeA, codeB)
? doSomething1Fn
: doSomething2Fn
)(param1, param2)
: null;
}
Next you could apply some currying (you could do it per parameter, but I think it's more useful in the blocks that will likely be used together):
function compareCodes(validateFn, moreProcessingFn, doStuffOnCodeAFn, doStuffOnCodeBFn, doSomething1Fn, doSomething2Fn) {
return function(codeA, codeB) {
return validateFn(codeA, codeB)
? moreProcessingFn(doStuffOnCodeAFn(codeA), doStuffOnCodeBFn(codeB), codeA, codeB)
? doSomething1Fn
: doSomething2Fn
: function(param1, param2) { return null; };
};
}
But that's about it. While it would be possible to write your own combinators for the conditionals and the parallel feeding of multiple arguments into multiple functions, you will not gain anything in the process. There certainly are no standard combinators like composition that would help you out here.
It might be a different thing if you drop out of always supplying two things together (A and B, 1 and 2) but as distinct parameters. If you instead modify all of your functions to take tuples instead (here represented as arrays of length 2, given JavaScript's lack of a pair type), we can do something. First we convert from
function compareCodes(validateFn, moreProcessingFn, [doStuffOnCodeAFn, doStuffOnCodeBFn], [doSomething1Fn, doSomething2Fn], [codeA, codeB], [param1, param2]) {
return validateFn([codeA, codeB])
? (moreProcessingFn([doStuffOnCodeAFn(codeA), doStuffOnCodeBFn(codeB)], [codeA, codeB])
? doSomething1Fn
: doSomething2Fn
)([param1, param2])
: null;
}
to (I'm using ES6 syntax, notable arrow functions and destructuring)
const bimap = ([f, g]) => ([x, y]) => [f(x), g(y)];
const fst = ([x, _]) => x;
const snd = ([_, y]) => y;
function compareCodes(validate, moreProcessing, doStuff, doSomething, code, param) {
return validate(code)
? (moreProcessing(bimap(doStuff)(code), code)
? fst
: snd
)(doSomething)(param)
: null;
}
Now that is something we can indeed tackle with combinators:
const compose = f => g => x => f(g(x));
const bind = f => g => x => f(g(x), x);
const cond = pred => then => other => x => pred(x) ? then(x) : other(x);
const k = x => _ => x;
function compareCodes(validate, moreProcessing, doStuff, doSomething)
return cond(validate,
cond(bind(moreProcessing)(compose(bimap)(doStuff)),
fst(doSomething),
snd(doSomething)
),
k(k(null))
);
}
We could go further to a completely point-free definition of compareCodes, but honestly that's not worth it.

Is `eval` the only way to create functions with dynamically determined arity in JavaScript?

I'm using a JavaScript spy library,
simple-spy.
I've found out that when spying on a given function,
the resulting spy always has an arity of 0.
This creates a problem with my use of
this currying function.
So I've submitted a pull-request
that adds arity transparency to
the spy library.
The code looks like this:
function spy(fn) {
const inner = (...args) => {
stub.callCount++;
stub.args.push(args);
return fn(...args);
};
// ends up a string like
// 'a,b,c,d'
// depending on the `fn.length`
const stubArgs = Array(fn.length)
.fill(null)
.map((m, i) => String.fromCodePoint(97 + i))
.join();
const stubBody = 'return inner(...arguments);';
// this seems to be the only way
// to create a function with
// programmatically specified arity
const stub = eval(
// the wrapping parens is to
// prevent it from evaluating as
// a function declaration
`(function (${stubArgs}) { ${stubBody} })`
);
stub.reset = () => {
stub.callCount = 0;
stub.args = [];
};
stub.reset();
return stub;
}
exports.spy = spy;
This seems to work.
Is it possible to do this
without the use of eval?
Is it possible to
reduce the use of eval
to even less that this?
I'm aware that there are other issues
with this spy implementation.
It is simplistic and it works
for my use case so far.
Like Benjamin wrote, I used a simple:
function spy(fn) {
const stub = (...args) => {
stub.callCount++;
stub.args.push(args);
return fn(...args);
};
stub.reset = () => {
stub.callCount = 0;
stub.args = [];
};
stub.reset();
Object.defineProperty(stub, 'length', {value: fn.length});
return stub;
}
exports.spy = spy;
Much, much better looking.

Accessors Composition in ES6 Classes

Let's say I have a Thing class which I want to be both Hideable and Openable.
Using a similar approach to Douglas Crockford's object creation through composition, I have been able to "inherit" from multiple classes.
This approach does not work with accessors (getter/setters).
I need to use classes as it's a requirement. I'm also finding that I am duplicating functionality from class to class, but I don't want these to inherit from a base class.
Any ideas?
The progress I have made so far is in the below snippet:
class Openable {
constructor(isOpen = false) {
this._isOpen = isOpen;
}
get isOpen() {
return this._isOpen + ' is stupid.';
}
set isOpen(value) {
this._isOpen = value;
}
}
class Hideable {
constructor(isHidden = false) {
this._isHidden = isHidden;
}
get isHidden() {
return this._isHidden + ' is stupid.';
}
set isHidden(value) {
this._isHidden = value;
}
}
class Thing {
constructor(config) {
let { isOpen, isHidden } = config;
let openable = new Openable(isOpen);
this.isOpen = openable.isOpen;
let hideable = new Hideable(isHidden);
this.isHidden = openable.isHidden;
}
}
let thing = new Thing({
isOpen: true,
isHidden: false
});
Because isOpen and isHidden are accessors, you can't just grab a copy of them, you have to access them when you want them.
Still, you can create your own isOpen, isHidden which use the underlying ones:
let openable = new Openable(isOpen);
Object.defineProperty(this, "isOpen", {
get: () => openable.isOpen,
set: value => {
openable.isOpen = value;
}
});
let hideable = new Hideable(isHidden);
Object.defineProperty(this, "isHidden", {
get: () => hideable.isHidden,
set: value => {
hideable.isHidden = value;
}
});
Live example on Babel's REPL
Naturally, if you do this a lot, you'd want to have a worker function to set that up rather than retyping it all the time:
function wrapProperty(dest, src, name) {
Object.defineProperty(dest, name, {
get: () => src[name],
set: value => { src[name] = value; }
});
}
(or do it by grabbing the property descriptor and updating it)
then:
wrapProperty(this, openable, "isOpen");
wrapProperty(this, hideable, "isHidden");
I'd question the requirement that you must use class for Openable and Hideable. They look much more like mixins to me.
Besides that the OP's accessor approach via "pseudo private property" notation and prototypal getters/setters for Openable/Hideable classes already is questionable, traits would come closest to the also doubtable requirement of using classes as mixin surrogates just for the sake of meeting documentation requirements.
As long as JavaScript does not provide traits natively, one has to stick to either more advanced class based mixin patterns ore one remembers Angus Croll's Flight Mixins.
A mixin's function body one has to write is close enough to the constructor body of a class. Nevertheless function based mixins never will be instantiated but always have to be applied to an object/type via either call or apply.
A possible solution, featuring this kind of mixin approach, that already reliably fulfills the OP's requirements then might look like the next provided example code ...
let
Openable = (function openableMixinFactory () {
let
defineProperty = Object.defineProperty,
isBoolean = (type => (typeof type == 'boolean'));
return function openableMixinApplicator (isOpen = false) {
let
openableCompositeType = this,
getIsOpen = (() => isOpen),
setIsOpen = (value => ((isBoolean(value) && (isOpen = value)) || (void 0)));
defineProperty(openableCompositeType, 'isOpen', {
get: getIsOpen,
set: setIsOpen,
enumerable: true
});
return openableCompositeType;
};
}()),
Hideable = (function hideableMixinFactory () {
let
defineProperty = Object.defineProperty,
isBoolean = (type => (typeof type == 'boolean'));
return function hideableMixinApplicator (isHidden = false) {
let
hideableCompositeType = this,
//getIsHidden = (() => isHidden),
getIsHidden = (() => [isHidden, 'is stupid.'].join(' ')),
setIsHidden = (value => ((isBoolean(value) && (isHidden = value)) || (void 0)));
defineProperty(hideableCompositeType, 'isHidden', {
get: getIsHidden,
set: setIsHidden,
enumerable: true
});
return hideableCompositeType
};
}());
class Thing {
constructor(config) {
let
{isOpen, isHidden} = config;
Openable.call(this, isOpen);
Hideable.call(this, isHidden);
}
}
var
thing = new Thing({ isOpen: true/*, isHidden: false*/ });
console.log('thing : ', thing);
console.log('thing.isOpen : ', thing.isOpen);
console.log('thing.isHidden : ', thing.isHidden);
console.log('(thing.isOpen = "xyz") : ', (thing.isOpen = "abc"));
console.log('(thing.isHidden = "xyz") : ', (thing.isHidden = "xyz"));
console.log('thing.isOpen : ', thing.isOpen);
console.log('thing.isHidden : ', thing.isHidden);
console.log('(thing.isOpen = false) : ', (thing.isOpen = false));
console.log('(thing.isHidden = true) : ', (thing.isHidden = true));
console.log('thing.isOpen : ', thing.isOpen);
console.log('thing.isHidden : ', thing.isHidden);
.as-console-wrapper { max-height: 100%!important; top: 0; }
Other answers of mine at SO, that provide similar solutions to related questions, featuring the same approach are ...
How do I organize data by common traits?
Composition/Inheritance/Factory - Best Pattern For This Case
Multiple inheritance using classes
Class inheritance between projects

Categories

Resources