I want to write a function whose specifications is described in the piece of code below which is the current implementation I have. It does work. However I have been trying for a while to write it pointfree and entirely as a composition of ramda functions and could not find a solution. The issue is linked to obj => map(key => recordSpec[key](obj[key]) which I cannot reduce in a way that I can write the whole thing pointfree.
How could I do?
/**
* check that an object :
* - does not have any extra properties than the expected ones (strictness)
* - that its properties follow the defined specs
* Note that if a property is optional, the spec must include that case
* #param {Object.<String, Predicate>} recordSpec
* #returns {Predicate}
* #throws when recordSpec is not an object
*/
function isStrictRecordOf(recordSpec) {
return allPass([
// 1. no extra properties, i.e. all properties in obj are in recordSpec
// return true if recordSpec.keys - obj.keys is empty
pipe(keys, flip(difference)(keys(recordSpec)), isEmpty),
// 2. the properties in recordSpec all pass their corresponding predicate
// For each key, execute the corresponding predicate in recordSpec on the
// corresponding value in obj
pipe(obj => map(key => recordSpec[key](obj[key]), keys(recordSpec)), all(identity)),
]
)
}
For instance,
isStrictRecordOf({a : isNumber, b : isString})({a:1, b:'2'}) -> true
isStrictRecordOf({a : isNumber, b : isString})({a:1, b:'2', c:3}) -> false
isStrictRecordOf({a : isNumber, b : isString})({a:1, b:2}) -> false
One way to achieve this would be to use R.where, which takes a spec object like your recordSpec and applies each predicate with the value from the corresponding keys of the second object.
Your function would then look like:
const isStrictRecordOf = recordSpec => allPass([
pipe(keys, flip(difference)(keys(recordSpec)), isEmpty),
where(recordSpec)
])
Related
I have a function to loop over an Array and look for a number.
function search(arr, num) { }
This function takes an Array and an Integer as the input. But I want the parameters to be of their respective types, is there any way I can set the type of the parameters.
if you want to enforce an specific data type you need to explicitly write that
/**
* looks for an specific item of the list.
*
* #param {Array<string>} data Array to search from.
* #param {number} num Number of where to find bla bla.
* #throws {TypeError} in case data or num do not have the expected type
* #returns {string} item found.
*/
export function search(data, num) {
if (!Array.isArray(data)) {
throw new TypeError("data should be an array");
}
if (typeof num !== "number") {
throw new TypeError("num should be a number");
}
return data[num];
}
JavaScript is a loosely typed language, there's no way to say declaratively that arr should be an array and num should be a number other than documentation (for instance, JSDoc). That would look like this:
/**
* Searches (insert fuller description here).
*
* #param {number[]} arr The array to search through.
* #param {number} num The number to (use? search for?)
* #returns (you can use the same {syntax} to say what the return type is)
*/
function search(arr, num) {
if (!Array.isArray(arr)) {
throw new Error(`'arr' argument must be an array`);
}
if (typeof num !== "number"/* Or use `Number.isInteger(num)` for an integer check [it includes the typecheck]*/) {
throw new Error(`'num' argument must be a number`);
}
// ...do the work...
}
You could check at runtime. That would look something like this:
function search(arr, num) {
if (!Array.isArray(arr)) {
throw new Error(`'arr' argument must be an array`);
}
if (typeof num !== "number"/* && perhaps a check for int if that part is really important*/) {
throw new Error(`'num' argument must be a number`);
}
// ...do the work...
}
There's a language built on top of JavaScript called TypeScript, which adds a static type system. It's then compiled to JavaScript for use with the browser, Node.js, etc. You may want to look into using that, if static typing is important for what you're doing.
In TypeScript, you'd do it like this (if you want the array to be an array of numbers):
function search(arr: number[], num: number) {
// ...
}
That doesn't require an integer, all JavaScript numbers are IEEE-754 floating point or BigInts.
javascript is a loosely type language. beacuse of it there are no types, you can actively check if the given parameter is array by .isArray() and .isInteger(), this both methods will return the booleen.
the answer given by pravat work but it can be devasting as they will not only set the type its will set its default value, in case if you didn't send the value of num then function will asume its 10 and start to work, which can be pain in debuging if you need strict type please look into typescript.
For example I have this function
function example(a=1,b=1,c=1,d=1,f=1) {
return (a*b*c*d*f)
}
So I have simple function with parameter which have default value.
And now when I call the function if I want to multiply a with f. I need to do that this way:
example(3,1,1,1,5)
Why I can't simply write first and last argument? Is that possible, something like:
example(3[1],5[5])
where numbers [1] and [5] determine the index of argument?
Why I can't simply write first and last argument...
Because that isn't how function calls are defined in the JavaScript specification. That's the only reason.
I would suggest passing an object whenever you are not sure about the number of argument you are going to pass to a function.
If I were in your place, I would have written it as follows in ES5 and earlier:
function example(params) {
var DEFAULT = 1
var a = params.hasOwnProperty('a') ? params.a : DEFAULT;
var b = params.hasOwnProperty('b') ? params.b : DEFAULT;
var c = params.hasOwnProperty('c') ? params.c : DEFAULT;
var d = params.hasOwnProperty('d') ? params.d : DEFAULT;
var e = params.hasOwnProperty('e') ? params.e : DEFAULT;
return (a * b * c * d * e);
}
console.log(example({a: 3, b: 5}));
In ES2015 and above (you're using default arguments, so I assume you're happy with ES2015 features), you can do the above more cleanly using a combination of default arguments and destructuring:
// This snippet requires that your browser support ES2015's default arguments and destructuring
function example({a=1,b=1,c=1,d=1,f=1}={}) {
// Note -------^-------------------^^^^
return (a*b*c*d*f);
}
console.log(example({a:3,f:5}));
There, we define that the actual argument will be an object, rather than discrete arguments, but we receive that object's properties as discrete arguments. We define that each property in the object has a default, and that the object iself is also optional (the ={} at the end). (You can leave off the ={} if you don't want to make example() a valid call.)
Question 1:
var _curry1 = function _curry1(fn) {
return function f1(a) {
if (arguments.length === 0) {
return f1;
} else if (a != null && a['##functional/placeholder'] === true) {
return f1;
} else {
return fn.apply(this, arguments);
}
};
};
What is the purpose of checking a['##functional/placeholder'] === true ?
Question 2:
http://ramdajs.com/0.18.0/docs/#reduce
How do I read the notation?
(a,b -> a) -> a -> [b] -> a
This is my first time seeing such notation, where does it come from?
Question 1:
There is no "notation". __.js should clear it up:
module.exports = {'##functional/placeholder': true};
so ##functional/placeholder is no different than foo in
a = { foo: true }
a.foo
a["foo"]
(Obviously, you can't write a.##functional/placeholder because of all the odd symbols there.)
The intent is also seen in that file:
/**
* A special placeholder value used to specify "gaps" within curried functions,
* allowing partial application of any combination of arguments,
* regardless of their positions.
*
* If `g` is a curried ternary function and `_` is `R.__`, the following are equivalent:
*
* - `g(1, 2, 3)`
* - `g(_, 2, 3)(1)`
* - `g(_, _, 3)(1)(2)`
* - `g(_, _, 3)(1, 2)`
* - `g(_, 2, _)(1, 3)`
* - `g(_, 2)(1)(3)`
* - `g(_, 2)(1, 3)`
* - `g(_, 2)(_, 3)(1)`
...
So the intent is to be able to "skip" some places when currying. The test decides whether an argument is a real argument or the __.js placeholder, and behaves accordingly. Why it is ##functional/placeholder - presumably precisely because it is hoped that it is too weird, and will thus not collide with anyone's legitimate data.
Question 2:
The notation is standard in type theory, and popularised by Haskell. a and b are any types. (...) is a tuple of types, [a] is an list whose elements are a. a -> b is a function that takes an argument of type a and yields a return of type b, and is right-associative. The example in question reads:
It is a function that takes an argument a function that takes two arguments (of types a and b respectively) and returns a value of type a; and yields a function that takes an argument of type a and returns a function that takes an argument that is a list of elements of type b, returning a value of type a.
This reads very confusingly, but an uncurried description will be a bit easier: it is a function that takes three arguments: the first one being a function (as described above), the second one being a value of a, the third one being a list of b elements, and returns a value of a.
Specifically, R.reduce is such a function: in
R.reduce(add, 10, numbers);
add is a function that takes two integers (both a and b being the same, integer), and returns an integer ((a, b) -> a); 10 is of type integer (a); numbers is a list of integers ([b]); and the return value is an integer (a).
Note that it mixes curried and uncurried syntax; if it was fully curried, add would be a -> b -> a, not (a, b) -> a.
Question 2:
That is a Hindley-Milner type signature. For the example given, 'a' and 'b' are arbitrary types and '->' is a function.
So lets break it down.
First, we take a type 'a' and a function 'b -> a'
(a, b -> a)
This returns a function, which returns a function,.. (cause you know, currying). The point is we can add '()' to make it a bit more readable.
(a, b -> a) -> (a -> ([b] -> a))
So if you pass this function 'a' and a function 'a' -> 'b', you get back
a -> ([b] -> a)
And if you pass this a type 'a'
[b] -> a
Where [b] is an array of type 'b's. Passing this function [b] gives you
a type a
a
If you want to read more about functional programming in JavaScript, I can recommend The Mostly Adequate Guide
I'm messing around with the type checking in google's closure compiler. The type system seems useful, if perhaps not the most sophisticated out there. I'm happy with most of the limitations, but this one just seems a bit weird.
I'm seeing problems giving type annotations for functions passed as arguments. In particular, if the type of the passed function is itself not fixed. So for example, I'd like to write code similar to this:
/**
* #param {Array} xs
* #param {function(*) : boolean} f
* #return {Array}
*/
var filter = function (xs, f) {
var i, result = [];
for (i = 0; i < xs.length; i += 1) {
if (f(xs[i])) {
result.push(v);
}
}
return result;
};
filter([1,2,3], function (x) { return x > 1; });
Passing "--js_error checkTypes" to the compiler, I get this:
test.js:17: ERROR - left side of numeric comparison
found : *
required: number
filter([1,2,3], function (x) { return x > 1; });
^
So, what's wrong? Can I specify that a parameter ought to a function with one argument, without specifying the type of that argument? Am I doing something wrong, or is this just a limitation of the type checker?
Chad suggests annotating the anonymous function passed to filter to help the type-inference out a bit:
filter([1,2,3], function (x) { return /** #type {number} */ (x) > 1; });
That works ok for filter(), but it seems a little unsatisfying (why does the compiler need that annotation?), and doesn't work for more complex cases. For example:
/**
* #param {Array|string} as
* #param {Array|string} bs
* #param {function(*, *): *} f
* #return {Array}
*/
var crossF = function (as, bs, f) {};
/**
* #param {Array|string} as
* #param {Array|string} bs
* #return {Array}
*/
var cross = function (as, bs) {};
var unitlist = crossF(['AB', 'CD'], ['12', '34'], cross);
It seems like the type of everything here should be apparent to the compiler. And in fact it complains directly about matching the type of the function parameter:
test.js:52: ERROR - actual parameter 3 of crossF does not match formal parameter
found : function ((Array|null|string), (Array|null|string)): (Array|null)
required: function (*, *): *
var unitlist = crossF(['ABC', 'DEF', 'GHI'], ['123', '456', '789'], cross);
Accepted answer below addresses this case.
Change the declaration of the filter from "*" (everything) to "?" (unknown). The compiler only checks known types. So when the compiler tries to infer the function signature for the function expression at the call site, it resolves the parameter "x" to "?" (an unknown type) (which can be used as anything), instead of "*" (every possible type) which often needs to be restricted before use:
/**
* #param {Array} xs
* #param {function(?) : boolean} f
* #return {Array}
*/
var filter = function (xs, f) {
var i, result = [];
for (i = 0; i < xs.length; i += 1) {
if (f(xs[i])) {
result.push(v);
}
}
return result;
};
When there are no annotations on a function, the compiler assumes that it can take a variable number of arguments of any type and return any type. For this reason, many of the extern functions are annotated like this:
/** #return {undefined} */
function MyFunction() {}
This way they will properly type check.
For your case, the easiest solution is to type cast the argument to a number inside the function (note the extra parenthesis which are required):
filter([1,2,3], function (x) { return /** #type {number} */ (x) > 1; });
One common approach is to use the type annotation {!Function}, which accepts any function object.
The issue with the ALL type (*) has been reported here: Issue 708
This looks like a bug to me. You should file it here:
http://code.google.com/p/closure-compiler/issues/list
If you don't specify the types, it should be "unknown"(?) not "any"(*). The compiler doesn't (or shouldn't) type check the use of unknown types.
I'm implementing a function that receives an argument which it needs to convert to its string representation.
If a given object implements a toString() method, then the function should use it. Otherwise, the function can rely on what the JavaScript implementation offers.
What I come up with is like this:
var convert = function (arg) {
return (new String(arg)).valueOf();
}
String(null) returns - "null"
String(undefined) returns - "undefined"
String(10) returns - "10"
String(1.3) returns - "1.3"
String(true) returns - "true"
I think this is a more elegent way.
I'm not sure you even need a function, but this would be the shortest way:
function( arg ) {
return arg + '';
}
Otherwise this is the shortest way:
arg += '';
value = value+"";
If targeting ES6 or later, you could use a template literal:
function (arg) {
return `${arg}`;
}
All data types in JavaScript inherit a toString method:
('hello').toString(); // "hello"
(123).toString(); // "123"
([1,2,3]).toString(); // "1,2,3"
({a:1,b:2}).toString(); // "[object Object]"
(true).toString(); // "true"
These are all great answers, but here comes the ultimate solution that covers all the cases :)
function stringify(value) {
switch (typeof value) {
case 'string': case 'object': return JSON.stringify(value);
default: return String(value);
}
};
for (const value of [
null,
undefined,
12345,
'abcdef',
true,
false,
BigInt(9007199254740991),
NaN,
Symbol('sym'),
[1, 2, 3],
{aaa: 'AAA', bbb: 'BBB'},
(a, b) => a + b,
stringify,
]) {
console.log(stringify(value));
}
The other answers are incomplete when it comes to a JSON object passed. So I made this one and it works for all:
var getString = (o) => {
if (o !== null) {
if (typeof o === 'string') {
return o;
} else {
return JSON.stringify(o);
}
} else {
return null;
}
}
JSON.stringify(value)
Works for null, undefined, primitives, arrays and objects — basically everything.
/* use back tic; same key as tilde(~) typically, and ${} */
const myString = `${value}`;
Short way to convert most types to String, but some types such as Objects will stringify their type [object Object]. For those
/**
* #typedef {Object} replacerProps
* #property {any} this - the replacer function.
* #property {string} key - the key of the current node passed to the replacer.
* #property {any} value - the data of the current node passed to the replacer.
*/
/**
* #param {any} value - data to stringify.
* #param {((replacerProps) => any)|undefined} [replacer=null] - A function
* that alters the behavior of the stringification process, or an array
* of String and Number that serve as an allowlist for selecting/filtering
* the properties of the value object to be included in the JSON string. If
* this value is null or not provided, all properties of the object are
* included in the resulting JSON string.
* #param {string|number} [space=null] - insert whitespace for readability.
* If null, no whitespace is used.
* #returns {string|undefined} a JSON string representing the given
* value or undefined.
*/
JSON.stringify(value, null, 0);
JSON.stringify() is the best catch-all solution.