thanks to the suggestion made by Volker I started thinking on how to add constants without a pre processor, after thinking some time I came with this:
# define a simple constant
$foo = 5
# will be inserted as is, it has to be a valid efene expression
$magic = A + B
# it will be called at runtime, not at compile time
$call = now()
# one constant can refer to constant defined before it in the same module
$nested = $magic + $foo
# run now() at compile time and generate the ast for the returned value
# note: too much magic here :)
$compile_time = $[now()]
@public
run = fn () {
A = 5
B = 6
io.format("hi! foo: ~p, magic: ~p, $now: ~p now(): ~p nested: ~p~n",
[$foo, $magic, $call, now(), $nested])
io.format("compile time: ~p~n", [$compile_time])
}
as you can see, constants are defined at module level with the same syntax than built-in constants ($module, $line, $module_name) after the equal sign it comes a valid efene expression, the content of the expression* is stored associated to the constant name and every time the constant is found in an expression in a place where a variable can be found it's replaced by the content of the expression.
the expression associated with the constant is inserted as is in the place where the constant reference was found, no compile time evaluation of any kind is done, this allows some magic like referring to variables that don't exist where the constant is defined but are taken from the context where the constant is replaced, it also allows to have expressions that do function calls that will be executed at run time.
even more magical is mixing constant definition with meta programming, in the last example we ask the compiler to run now() at compile time and generate the AST for the returned expression, this is set as the value of the constant that when replaced will insert a 3 item tuple with the time when the module was compiled.
to see how this works, here is the output of asking the efene compiler to compile to erlang code:
-module(hello).
-export([run/0]).
run() ->
A = 5,
B = 6,
io:format("hi! foo: ~p, magic: ~p, $now: ~p now(): "
"~p nested: ~p~n",
[5, A + B, now(), now(), A + B + 5]),
io:format("compile time: ~p~n",
[{1313, 102900, 194901}]).
as you can see there are no traces of the constant definitions in erlang, the values are inserted where the constants were used.
the interesting thing is that the constant are inserted as the exact expression they are set to, no compile time evaluation is done (except you use meta eval or similar).
I'm showing this to ask for comments on the implementation before it stays as the official way of defining constants.
My opinion is that it's a nice way to define constants, it's consistent with built-in constants, it doesn't introduce new syntax and leaves room for some black magic when needed :)
Now it's time for your opinion!
extras, nice error reporting:
the expression C = $i_dont_exist generates the error "line 23: undefined constant i_dont_exist"
redefining a variable "line 13: constant foo already defined at line 3"
also syntax errors in the constant expressions are reported as normal syntax errors
* actually the abstract syntax tree of the expression for language geeks
Hi,
ResponderEliminarthe proposal sounds good to me, it solves the problem that I needed a way to define constants. Though I think the "too much magic" stuff is really to much magic. I wouldn't include the $[] syntax.
One thing to keep in mind. Will the syntax still work once header files are supported? I'd say yes.
Cheers,
Volker
the $[] is already in the language, see
ResponderEliminarhttp://marianoguerra.com.ar/efene/docs/reference/expressions/metaeval.html
http://marianoguerra.com.ar/efene/docs/reference/expressions/astify.html
http://marianoguerra.com.ar/efene/docs/reference/expressions/metaevalandastify.html
I was just showing how they can be used together, of course it's advanced and should be used that much, but jsut to show some cool tricks :)
what do you mean by "once header files are supported"?
includes?
With header files I mean includes (Erlangs .hrl files).
ResponderEliminarread the new post on includes.
ResponderEliminaras it's now, constants on one module work only on that module, even if you include other modules in that module, they will be seen on that module only.
I think it's the best thing to do, what do you think?