reusing specified code as building blocks for other specs ?
Hi,
thanks for providing this, it's really awesome - I skimmed through the buildingBlocks.js and the tests and could not find an answer to my question: is there a way to reuse a spec as the building block for another spec, i.e. so that specs can be incrementally "solved" ?
Besides, is there a way let it use named arguments ?
Thanks
Just to clarify, the idea is to run a specify(), let that solve and then register that as a building block for use in other specify() calls - preferably, without having to save anything to disk
Looking at the docs, I think I am really asking about this:
specifications are composable, so inductive.js can generate programs of any size or complexity.
@UniqueFool it's still in an experimental state and rough around the edges but I'm glad you like it :)
You can see a lot of the features run through their paces in testSolves.i.js. To compose specs take a look at usages of the doubleIt spec. The following things are tested/demonstrated:
- Passing
doubleItas a parameter in test cases (specArgCaller,specArgCallerExplicit,callOverOne) - Using
doubleItas a building block of another spec (double2,mockDouble2Log,chainSpecs) and indirectly (deepChainSpecs). - Using
doubleItas a building block and mocking it (mockSpecs).
is there a way let it use named arguments
Unfortunately I haven't implemented anything along these lines, but it's doable.
thanks for getting back in touch, I have been looking exactly at the files you mentioned: buildingBlocks.js and testSolves - I think you mentioned that in one of the readmes.
In fact, I also looked at the doubleIt specs specifically, because I ended up using the factorial as an example that I adapted.
Named arguments would be "icing on the cake" - so mainly a matter of making the output more useful for human programmers. So if it is difficult, ignore that idea ;-)
That being said, this is really cool, I just to find more examples that I can adapt to learn how everything works.
For instance, I just spent 1 hour trying to figure out how to make it use a tryCatchFinally block in conjunction with a callback and an argument that may throw an exception, wanting it to return true/false.
I specifically looked at your RegExp examples, and was trying to adapt those to come up with a spec that checks if a regex is valid or throws an exception, i.e. in the form of::
function isValid(string) {
try {
RegExp(string)
return true;
}
catch(e) {
return false;
}
}
Any ideas ?
Thanks again
PS: A little explanation regarding the annotation syntax for those as() calls would be also nice to have
Named arguments would be "icing on the cake"
I haven't been working on this much recently. The main outstanding item for me is that the type system needs to be refactored. I will add this to my todo list though.
A little explanation regarding the annotation syntax for those as() calls would be also nice to have
as has one of two purposes. Either it's used to label test cases, or it's inside of express, which is used to create building blocks from scratch (essentially via a macro system).
Here is an example of using as to label a test case:
specify('add',
given(1, 2,
shouldReturn(3),
as('adding 1 and 2 should return 3')),
use('Number+'))
The output would look like this:
ok 1 - add : (Number, Number) -> Number solution 0.012s
ok 2 - add(1, 2) adding 1 and 2 should return 3
We can use as to create a macro as well. For the isValid case you've described, I haven't set up a building block for the RegExp constructor. It's easy to do by using express and as however:
var stringToRegExp = express('stringToRegExp',
takes(String),
returns(RegExp),
as('RegExp(@String)'))
takes says the expression requires a String expression and returns says the result of evaluating the expression is a RegExp. as provides the actual string to use in the macro. It's a little like an html template. The @String part tells inductive.js where to place the string expression that stringToRegExp uses (which we specified in takes).
As a side note, as also provides some syntactic sugar in expressions. We can also define macros using block-comments:
var stringToRegExp = express('stringToRegExp',
takes(String),
returns(RegExp),
as(function() { /* RegExp(@String) */ }))
The way this works is inductive.js calls toString on the function passed to as and gets the template string that way. This is useful for writing multi-line expressions.
To answer your question about isValid, the following works. (I added comments to explain what's going on.)
// Name the function
specify('isValid',
// Since some of our test cases throw we have to tell inductive.js
// to ignore this. Normally inductive.js will stop searching if it
// encounters an error.
setOptions({ allowErrors: true }),
// This is an invalid RegExp so it should return false
given('[', shouldReturn(false)),
// This is valid so it should return true
given('abc', shouldReturn(true)),
use(
// We need the expression we created earlier
stringToRegExp,
// Use the `tryCatch` building block
'tryCatch',
// We are using constants for `true` and `false`:
value(true),
value(false),
// We are invoking the `RegExp` constructor for its side-effect.
// The comma operator lets us ignore the result of `RegExp`
// and return a boolean. In the generated function
// you will see something like `return (RegExp(arg0), true)`
',',
// The way I have set up the `tryCatch` building block is that it requires
// a function to handle the error in the `catch` clause.
// This building block, `functionExpressions()`, tells inductive.js
// to write function expressions out anywhere it runs into a building block
// that needs a function.
functionExpressions()))
If we set the verbosity to 1 the output looks like this:
# function isValid(arg0) {
# return (function() {
# try {
# return (RegExp(arg0), true)
# } catch(err) {
# if(typeof err !== 'object' || !(err instanceof Error))
# err = new Error(err)
# return (function fn(arg1) {
# return false
# })(err)
# }
# })()
# }
ok 1 - isValid : String -> Boolean solution 0.343s
ok 2 - isValid("[") should return false
ok 3 - isValid("abc") should return true
An explanation of the generated program:
function isValid(arg0) {
return (function() {
try {
// Invoke RegExp for its side-effect then return true
return (RegExp(arg0), true)
} catch(err) {
if(typeof err !== 'object' || !(err instanceof Error))
err = new Error(err)
// The `tryCatch` building block uses the function expression here:
return (function fn(arg1) {
// arg1 is of `Error` type. We don't use it in this program however.
return false
})(err)
}
})()
// The reason `tryCatch` is wrapped in an IIFE is that this is my solution to make
// it act as an expression (with a return value), not a statement.
// All building blocks in inductive.js have to be expressions.
}
For reference, this is how the tryCatch building block is defined:
express('tryCatch',
takes(typeParameter('a')),
takes(
functionType(
takes(Error),
returns(typeParameter('a'))), '@catchFn'),
returns(typeParameter('a')),
as(function() { /*
(function() {
try {
return @'a
} catch(err) {
if(typeof err !== 'object' || !(err instanceof Error))
err = new Error(err)
return (@catchFn)(err)
}
})()
*/ }))
hey, thanks so much for taking the time to share all this - that will keep me busy for a while, I was actually thinking of the as syntax as some kind of templating mechanism, but it is good to have an actual explanation now. I would suggest to add your last response to the Readme.md, so that others can more easily find this valuable information !
Besides, thanks also for specifically going into so much detail and covering my specific use-case, it's really appreciated !