erl_scan:string/1, erl_parse:parse_exprs/1, and erl_eval:exprs/2, and simple string evaluation can produce some pretty neat results - including a form of function currying. Basics
Here are some basic functions - I've put them into a module called
meta:
run(Bindings,Block) ->
evaluate(expressions(Block),Bindings).
expressions(String) ->
{ok,Tokens,_} = erl_scan:string(String++"."),
{ok,What} = erl_parse:parse_exprs(Tokens),
What.
evaluate(Exprs) -> evaluate(Exprs,[]).
evaluate(Exprs,Bindings) -> element(2,erl_eval:exprs(Exprs,Bindings)).
The
run/2 function takes a set of bindings and a block of code that is to be interpreted as a collection of erlang expressions. It then parses the string for expressions and evaluates them with the provided bindings. Here are some basic examples:
1> meta:run([{'X',10}],"X").
10
2> meta:run([{'X',10}],"X+X").
20
3> F = meta:run([{'X',10}],"fun() -> X+X end").
#Fun
4> F().
20
5> meta:run([{'X',10}],"lists:seq(1,X)").
[1,2,3,4,5,6,7,8,9,10]
6> meta:run([{'Start',10},{'Stop',20}],"lists:seq(Start,Stop)").
[10,11,12,13,14,15,16,17,18,19,20]
Example: Function Currying
These functions also allow for a surprisingly simple
meta:curry/2 function. Here are some examples of currying:
8> Even = meta:curry(fun lists:filter/2,[fun(X) -> X rem 2 == 0 end]).
#Fun
9> Even([1,2,3,4,5]).
[2,4]
10> Foo = meta:curry(fun(A,B,C,D,E) -> A+B+C+D+E end,[1,2,3]).
#Fun
11> Foo(4,5).
15
And here is the code for the curry function:
curry(Fun,Args) ->
{arity,Arity} = erlang:fun_info(Fun,arity),
PList = string:join([randvar() || _N<-lists:seq(1,Arity-length(Args))],","),
run([{'Args',Args},{'Fun',Fun}],
"fun("++PList++")->apply(Fun,Args++["++PList++"]) end").
Though this isn't true currying, it functions similarly and takes some of the work out of writing wrapper funs by hand. Oh, the
randvar/0 function just produces a string that can be interpreted as a variable - it was inspired by lisp's gensym.Dynamic Modules
Also, with some work, modules can be written and loaded at runtime. The functions of interest are
erl_parse:parse_form/1 for parsing function definition strings, compile:forms/1 for actually compiling lists of abstract forms, and finally code:load_binary/3 for loading your compiled forms as a module. I haven't codified much of this yet, mostly because I haven't found a specific use, but here is a basic run:
1> String = "my_function(Var1,Var2) -> Var1*Var2.",
1> {ok,Tokens,_} = erl_scan:string(String),
1> {ok,Form} = erl_parse:parse_form(Tokens),
1> Attributes = [{attribute,1,module,my_module},{attribute,1,compile,[export_all]}],
1> {ok,Module,Binary} = compile:forms(Attributes++[Form]),
1> code:load_binary(Module,atom_to_list(Module)++".erl",Binary).
{module,my_module}
2> my_module:m
module_info/0 module_info/1 my_function/2
2> my_module:my_function(10,10).
100
This is a pretty fun topic, and I have no doubt that more posts will continue.