Anonymous function proposal
fn int test(int x)
{
fn int square(int y)
{
return y * y;
}
return square(x) + square(x + 1);
}
Feel free to vote on this one using 👍 and 👎
Alternative syntactic forms:
fn int test(int x)
{
const square = fn int(int y) { return y * y; }
return square(x) + square(x + 1);
}
Which then would allow things like:
fn int test(int x)
{
return do_something(fn int(int y) { return y * y; });
}
Please note that these are not closures, so nested functions cannot access the variables of the enclosing function.
Another open question is whether such anonymous functions could be declared anywhere in the scope of a function, or only at the top level:
fn void test()
{
// Always ok
fn void foo() { ... }
for (int i = 0; i < 10; i++)
{
// Is this ok?
fn void bar() { ... }
}
}
// Is this ok? fn void bar() { ... }
:point_down:
And an anonymous macros too!
macro int test(int x)
{
macro int square(int y)
{
return y * y;
}
return @square(x) + @square(x + 1);
}
To be honest, I'm not particularly keen on adding macros. I have a hard time motivating the functions...
In Nim language unit testing implemented as macros:
suite "description for this stuff":
echo "suite setup: run once before the tests"
setup:
echo "run before each test"
teardown:
echo "run after each test"
test "essential truths":
# give up and stop if this fails
require(true)
test "slightly less obvious stuff":
# print a nasty message and move on, skipping
# the remainder of this block
check(1 != 1)
check("asd"[2] == 'd')
test "out of bounds error is thrown on bad access":
let v = @[1, 2, 3] # you can do initialization here
expect(IndexDefect):
discard v[4]
echo "suite teardown: run once after the tests"
I think this approach can be easily implemented in C3 using anonymous macros.
I think this approach can be easily implemented in C3 using anonymous macros.
How?
How?
Something like this:
module testing;
extern fn void printf(char*,...);
macro void unittest(#name; @body())
macro void expect(#condition)
{
if (#condition)
{
printf("Test [%s] passed\n", $stringify(#condition));
}
else
{
printf("Test [%s] failed\n", $stringify(#condition));
}
}
macro void suite(#name; @body())
{
printf("Suite %s\n", $stringify(#name));
@body();
}
{
printf("Testing %s\n", $stringify(#name));
@body();
}
fn void main()
{
@unittest(aaa,
@suite(comparisons,
@expect(1==1);
@expect(1!=1))
);
}
For now, only expect(#condition) works in this PoC. :)
Outstanding questions:
- What happens when a macro contains an anonymous function and it is inlined?
macro foo(int x)
{
fn int square(int x)
{
return x * x;
}
return square(x) + x;
}
Now consider this use:
fn test(int a, int b)
{
return foo(a) + foo(b);
}
I the macro is naively inlined, then we get two copies of "square" here. A better solution would perhaps be that the function is never duplicated.
- Continuing on (1) what about this:
macro foo(a)
{
var $Type = $typeof(a);
fn $Type square($Type a)
{
return a * a;
}
}
If the above would be valid, then we're essentially creating generic functions by folding an inner function inside of a macro. While this might be useful, I think it's preferable to have something like:
macro foo(a) @noinline { ... }
// or
generic foo(a) { ... }
- If one wants that functionality.
-
Another possibility is obviously to not allow nested functions in macros at all.
-
If nested functions are not allowed in macros, then the question if they are allowed in defers must also be considered.
@data-man Note that this would not work:
@unittest(aaa,
@suite(...)
);
Because the @suite here would be evaluated in the calling scope, not in the macro's scope.
foo = fn int(int y, double d) { return (int)(y * d); }; added.