a .NET hosted scripting language with a focus on meta-programming and embedded DSLs
One of the reasons I never liked C/++ is it's illogical pointer syntax.
int v0=5,v1=6;int* p0;
int *p1;
p0 = &v0;
*p0 = v1;
The first pointer declaration is ok: The star turns the type integer into a pointer-to-integer.The second one is a bit confusing. To the readers (at least mine) eye it looks like the star belongs to the identifier, modifying it somehow. Ok, it's just the result of C/++ being a freeform language so let's go on. The star turns any data type T into a pointer-to-T. So the same must be true for values; turning values into pointers to those values, right? Bad luck. The inventor of C chose to use a different symbol for returning the address of a value. Fine with me, as long as the star has no other appearances in the indirection business.
But now it comes, the ultimate anti-logic: The star acts as the dereferencing operator, the exact opposite of it's meaning in declarations. The fact that the star usually appears near the identifier and thus looks identical to an indirect access to it's target in expressions does not make learning C/++ easier. Why am I telling you this? When defining the Prexonite Script syntax, I had to decide how to represent references.
I figured that a reference is "something that points at something else".If we assume that something and something else are variables, a more general statement might be variable#1
that points at variable#2
. Now let's replace the "that" with an assignment operator and we end up with the unary prefix operator "points to" and I hope you agree with me that there is no better way to represent "points to" than with an arrow, pointing at the right hand side. So in the end, we have the this syntax
var v0 = 5;
ref f0 = f(x) => print(x);
ref r0 = ->v0;
print(r0);
ref r1;
->r1 = ->f0;
r1 = f0; //syntactically correct
r0 = ->f0;
//Experimental:
r1 -> f0;
The first two lines are easy: var v0 = 5;
declares and initializes a (data) variable and ref f0 = f() => print(v0);
declares and initializes a reference variable (a function reference, actually) with an anonymous function.
Following those is the declaration and initialization of a (data) reference variable r0
initialized with a reference to v0 as well as a command call print(r0);
.
Now, you may wonder why I am passing the reference to v0
to a command call.
The reason is that Prexonite Script automatically dereferences every reference variable, so you can use it as if it was a normal function.
I could have written f0(r0);
instead of print(v0);
. I'll write a post about the philosophy behind auto-dereferencing.
The sixth line (->r1 = ->f0;)
looks a bit more complicated but you already know the reference operator, the unary prefix ->
.
You have to use the reference operator on the left hand side because you don't want to assign to the target of r1
(your reference) but to the reference itself.
Similar reason for the operator on the right hand side: You want to assign "the thing behind" f0
and not the result of calling f0
(or the function behind).
Let's look at the seventh statement (r1 = f0;
), which is syntactically correct but will not do anything at runtime.
The compiler will translate this into a indirect "set"-call to the function referenced by r1
with one argument, the result of a "get"-call to the function referenced by f0
.
Now since we haven't assigned any function to r1
, this line will just do nothing (note that f0
still get's evaluated).
If you want to assign the target of f0
to r1
and not the result of a call to f0
, again you have to use the reference operator on both the left and the right hand side.
You could read this line as "the thing r0
is pointing to is the thing f0
is pointing to".
The next line (r0 = ->f0;
) actually uses indirection.
Remember that r0
points to the variable v0
.
So every call to r0
is being forwarded to v0
.
What this statement does, is assigning the reference to f0
(which is the function behind f0
) to v0
(the target of r0
).
The last line, finally, (r1 -> f0;
) is an experimental syntactic sugar for ->r1 = ->f0
.
Please note that ->
cannot be applied to any expression but only to known symbols, so var x = ->(1 + 1)
will not be recognized.
We could talk about dynamic dereferencing à la var x = ->("f0")
but as most of the symbols get thrown away at the end of the build process, this would not make much sense.