I'm trying to write a simple Id monad to play around with and learn.
Here is what I've written:
const util = require('util');
const Id = x => ({
[util.inspect.custom]: () => `Id(${x})`,
map: f => Id.of(f(x)),
flatMap: f => f(x),
valueOf: () => `Id(${x})`,
});
Id.of = Id;
const foo = Id.of('foo');
const toUpper = str => Id.of(str.toUpperCase());
const fooBoxed = foo.map(toUpper); // Oh oh, should be Id(Id('FOO'));
const FOO = foo.flatMap(toUpper); // Yay, Id('FOO');
console.log(fooBoxed);
console.log(FOO);
fooBoxed should log out Id(Id(Foo)), but it logs out Id([object object]).
I tried modifying valueOf and inspect but both doesn't work. I suspect that ${x} calls a different method and I couldn't find on the internet which that is. What do I have to modify so that ${x} returns the correct string for my nested Id monad?
You will want to overwrite toString, not valueOf. Concatenating with a string (or interpolating a template string) will coerce the value to a string, and your objects inherit Object.prototype.toString which returns [object …].
const Id = x => ({
toString: () => `Id(${x})`,
map: f => Id.of(f(x)),
flatMap: f => f(x),
});
Id.of = Id;
const foo = Id.of('foo');
const toUpper = str => Id.of(str.toUpperCase());
const fooBoxed = foo.map(toUpper);
const FOO = foo.flatMap(toUpper);
console.log(fooBoxed.toString()); // Yay, Id(Id('FOO'));
console.log(FOO.toString()); // Yay, Id('FOO');
However, since you seem to intend to use this for debugging, you should actually use the debugging representation of your x value. To do that, call util.inspect() yourself:
const util = require('util');
const Id = x => ({
[util.inspect.custom]: () => `Id(${util.inspect(x)})`,
// ^^^^^^^^^^^^
map: f => Id.of(f(x)),
flatMap: f => f(x),
valueOf: () => x,
});
Id.of = Id;
Related
Suppose that you have the following code:
import R from "ramda";
import S from "sanctuary";
import { Left, Right } from "sanctuary-either";
const add = R.curry((p1, p2) => p1 + p2);
const addOne = add(1);
const func1 = () => Right(2);
const func2 = () => Right(7);
Combining addOne with func1 or func2 is relatively easy:
const res = R.compose(
S.map(addOne),
func1
)();
but how can one call add using func1 and func2 as arguments?
p.s. I know that ramda offers an add function. Consider the example as an abstraction of a real world problem.
You are probably looking for the lift2 function:
const addEithers = S.lift2(add)
console.log(addEithers(func1())(func2()))
Alternatively, you can use ap:
S.ap(S.map(add)(func1()))(func2())
I have the following function:
const setDefaults = (func, defArgs) => {
//should return the func with default arguments modified
// so that in case that an argument is not passed to the function,
// but it is provided in default Argument, the default argumnt is used
}
func: is a function that needs to have default parameters assigned from the defArgs
defArgs: set of default arguments
For example:
const greet = name => `Hi, ${name}!`
setDefaults(greet, {name: 'friend'})
greet(); // Hi, friend!
So far I have started diving into func.toString() and thinking about modifying the original function as a string and then eval the output, but that seems a bit verbose, so I was wondering if there is any better way to do this.
greet(); // Hi, friend!
You can't modify the original function because it's a const. If you mean to do something like this:
const greet = name => `Hi, ${name}!`
const parasiticGreet = setDefaults(greet, {name: 'friend'})
parasiticGreet(); // Hi, friend!
It is possible but I would simplify it like this:
const greet = name => `Hi, ${name}!`
const setDefaults = (func, defArgs = []) => (...args) => func(...defArgs.map((x, i) => args[i] === undefined ? x : args[i]));
const parasiticGreet = setDefaults(greet, ['friend']);
console.log(parasiticGreet()); // Hi, friend!
Here's my callnapply.js file
const callAndApply = {
caller(object, method, nameArg, ageArg, tShirtSizeArg) {
method.call(object, nameArg, ageArg, tShirtSizeArg);
},
applier(object, method, argumentsArr) {
method.apply(object, argumentsArr);
},
};
module.exports = callAndApply;
And here's a snippet from the test file which contains the non-working test:
const callnapply = require('./callnapply');
test('testing Function.prototype.call as mock function', () => {
const outer = jest.fn();
const name = 'Aakash';
const age = 22;
const tee = 'M';
callnapply.caller(this, outer, name, age, tee);
expect(outer.call).toHaveBeenCalledWith(name, age, tee);
});
How do I write the test to check if the method that I am passing is, in fact, being called by the Function.prototype.call function only? I want to check whether .call() is being called and not some other implementation that has been written for the method call.
You can mock the .call() method itself:
const outer = function() {}
outer.call = jest.fn()
Then you can do usual jest mock tests on outer.call.
Here is the working test file:
const callnapply = require('./callnapply');
test('testing Function.prototype.call as mock function', () => {
const outer = function() {};
outer.call = jest.fn();
const name = 'Aakash';
const age = 22;
const tee = 'M';
callnapply.caller(this, outer, name, age, tee);
expect(outer.call).toHaveBeenCalledWith(this, name, age, tee);
});
See this working REPL.
If possible, what is the simplest way to return a named arrow function?
const add = a => b => b + a
const add5 => add(5)
add.name == 'add' // true
add5.name == '' // true, but I would like it to be 'add5'
So, as one can see the in example above, the returned arrow function is anonymous and I would like it to be named (ideally based on the 'parent' function and the 'parameter' a) — which is useful i.e. for debugging.
You can do this:
const add = a => (({[`add${a}`]: b => b + a})[`add${a}`]);
const add5 = add(5);
console.log(add5.name);
How it works: define a local object and assign the arrow method as a member with your desired name:
const add = a => {
const o = {
[`add${a}`]: b => b + a
};
return o[`add${a}`];
};
This isn't exactly the same, but:
const add = a => b => a + b;
const add5 = (...a) => add(5)(...a);
console.log(add5(100)); // => 105
console.log(add5.name); // => 'add5'
div{min-height:100%}
This example demonstrates how arrow function names are assigned, and this behaviour cannot be changed. Arrow function name is equal to the name of a variable or object property it was assigned to.
name is non-writable property but is configurable in most implementations, including evergreen browsers, so it's safe to use for debugging purposes:
function namedArrow(name, fn) {
Object.defineProperty(fn, 'name', { enumerable: false, value: name });
return fn;
}
const foo = namedArrow('foo!', () => { throw new Error });
foo();
It outputs:
[
For debugging purposes displayName can be used also, which is supposed to play same role and is supported by some debuggers and browser debugger tools, notably Firefox and Chrome:
function namedArrow(name, fn) {
fn.displayName = name;
return fn;
}
It results in similar output, yet it doesn't play well with Error call stack:
I'm unable to figure out why Flow cannot correctly infer the return type from the Reference #1 map function if Reference #2 is not commented out.
If Reference #2 is commented out OR if I explicitly denote Mappable<B> as the return type for Reference #1 all is good.
const map = <A, B>(transform: Transform<A, B>, mappable: Mappable<A>): Mappable<B> => mappable.map(transform);
Would love to know why this is working the way it is! Thanks!
// Types
type Transform<A, B> = (value: A) => B;
// Classes
class Mappable<A> {
__value: A;
constructor(value: A) {
this.__value = value;
}
map<B>(transform: Transform<A, B>): Mappable<B> {
return new Mappable(transform(this.__value));
}
}
class Container<A> extends Mappable<A> {}
// Transformations
const stringToBase10Number = (value: string): number => parseInt(value, 10);
const numberToString = (value: number): string => value.toString();
// Map Utility (Reference #1)
const map = <A, B>(transform: Transform<A, B>, mappable: Mappable<A>) => mappable.map(transform);
// Log Utility
const log = <T>(value: T): T => { console.log(value); return value; }
// Test Case
const fooContainer = new Container('10');
const fooNumberContainer = map(stringToBase10Number, fooContainer);
map(log, fooNumberContainer); // Logs '1' to the console
// Reference #2
const fooStringContainer = map(numberToString, fooNumberContainer);
map(log, fooStringContainer);
Error:
const fooStringContainer = map(numberToString, fooNumberContainer);
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function call
const map = <A, B>(transform: Transform<A, B>, mappable: Mappable<A>) => mappable.map(transform);
^ B. This type is incompatible with
const numberToString = (value: number): string => value.toString();
^^^^^^ number
Try adding a return type to the map function. So change
const map = <A, B>(transform: Transform<A, B>, mappable: Mappable<A>) => mappable.map(transform);
to
const map = <A, B>(transform: Transform<A, B>, mappable: Mappable<A>): Mappable<B> => mappable.map(transform);
Your full example before
Your full example after
Why is this return type annotation needed? It's because Flow does not infer polymorphic types. If the function always returned a string, it wouldn't matter. But since it returns Mappable<B>, the annotation is needed.
Here is another answer that also addresses this problem: Polymorphic Anonymous Functions Type Aliases