Reputation: 93
New to JavaScript, so give me some leeway.
I'm currently trying to simulate contracts in JavaScript. There are preconditions and postconditions which can be defined when you declare a function. I've compiled a simple setup of how it should work below.
Function mult() has a few contracts which should check if the two inputs are numbers and should check if the resulting output is also a number.
//CONTRACT
function isNumber(v) {
return !Number.isNaN(v) && typeof v === 'number';
}
isNumber.expected = "number";
/**
* @param preList Array of contracts to check
* @param post Expected result type as a contract
* @param f the function
*/
function contract (preList, post, f) {
//??? No idea
//precondition
for (let i = 0; i < preList.length; i++) { //THIS DOESNT WORK
let valid = preList[i].call(this, f.arguments[i]);
if (valid === false) throw "caller's fault";
}
//postcondition
let result = f.call(this, f.arguments);
if ((post.call(this, result)) === false) throw "library's fault";
}
//EXAMPLE
var mult = contract(
[isNumber, isNumber],
isNumber,
function mult (x, y) {
return x*y;
});
//TEST CODE
console.log("First test")
console.log(mult(3, 4));
console.log();
console.log("Second test")
try {
console.log(mult(3, "four"));
} catch (e) {
console.log(e.message);
}
console.log();
I'm fairly sure a lot of the contract(){} function is wrong and after a lot of trial and error I have ended up here.
Currently I'm trying to loop through all the preconditions and call them with the arguments of f. From my understanding, (arguments) refers to the arguments of the current function, but I'm looking for the arguments of f, not of contract. f.arguments
I've also tried to do this with JavaScript proxies which can intercept function calls by setting traps for apply and construct but have gotten stuck as well.
Any help and pointers would be greatly appreciated. Using node.js to run this program.
Upvotes: 0
Views: 246
Reputation: 93
Now that the due date has passed, here's my solution. I wasn't supposed to be returning a value directly, but a function which would return a value after checking the input parameters and the output.
function contract (preList, post, f) {
return function() {
for (let i = 0; i < preList.length; i++) {
let valid = preList[i].call(this, arguments[i]);
if (!valid)
throw new Error("Contract violation in position " + i + ". Expected " + preList[i].expected + " but received " + arguments[i] + ". Blame -> Top-level code");
}
let result = f.apply(this, arguments);
if (!post(result))
throw new Error("Contract violation. Expected " + post.expected + " but returned " + result + ". Blame -> " + f.name);
return result;
}
}
Upvotes: 1
Reputation: 899
You can try something like this:
"use strict";
function isNumber(v) {
return !Number.isNaN(v) && typeof v === "number";
}
/**
* @param preList Array of contracts to check
* @param post Expected result type as a contract
* @param f the function
* @param array: arguments passed to function f
*/
function contract(preList, post, f, args) {
for (let i = 0; i < preList.length; i++) {
let valid = preList[i].call(this, args[i]);
if (valid === false) throw "caller's fault";
}
let result = f.apply(this, args);
if (post.call(this, result) === false) throw "library's fault";
return result;
}
let mult = function(x, y) {
let f = function(x, y) {
return x * y;
};
return contract([isNumber, isNumber], isNumber, f, arguments);
};
console.log("First test");
console.log(mult(3, 4)); // 12
console.log("Second test");
try {
console.log(mult(3, "four"));
} catch (e) {
console.log(e); // caller's fault
}
Upvotes: 2