Erlang for Python programmers: Part IV
Previous parts: Intro, Part I, Part II and Part III
Funs - sequel
Continuing previous post about functions let’s remember about Fun. I already mentioned it in Part I and promised to show its advanced forms. So, here we go.
Fun with one clause:
1> Double = fun(X) -> X * 2 end. #Fun<erl_eval.6.56006484> 2> Double(2). 4
But Fun actually has the same function declaration syntax as regular function, except that it has no name in declaration, so we can have this:
3> Big = fun (X) when X > 100 -> true; 3> (X) -> false 3> end. #Fun<erl_eval.6.56006484> 4> lists:filter(Big, [20, 30, 120, 130]). [120,130]
As you see our Fun has two clauses separated by semicolon(;) and first clause has also guard, ie syntax is the same as in regular function declaration.
There are also following fun expressions that can be used:
- fun FunctionName/Arity (FunctionName should point to local function defined in the same module with our fun)
- fun Module:FunctionName/Arity (FunctionName should be exported from module Module)
fun FunctionName/Arity is just syntactic sugar for fun (X1, ..Xn) -> FunctionName(X1, .., Xn) end.
Let’s take a look on foregoing in action and create mymath.erl
-module(mymath). -export([test1/0, test2/0, test3/0, double/1]). test1() -> lists:map(fun(X) -> X * 2 end, [1, 2, 3]). test2() -> lists:map(fun double_local/1, [1, 2, 3]). test3() -> lists:map(fun mymath:double/1, [1, 2, 3]). %% helper function, not exported double_local(X) -> X * 2. %% helper function, exported double(X) -> X * 2.
Compile and test it (I’m compiling as usual in Emacs with C-c C-k):
1> c(”/home/alienoid/dev/erlang/mymath”, [{outdir, "/home/alienoid/dev/erlang/"}]). {ok,mymath} 2> mymath:test1(). [2,4,6] 3> mymath:test2(). [2,4,6] 4> mymath:test3(). [2,4,6]
Also tuple of type {Module, FunctionName} is interpreted as a fun Module:FunctionName/Arity which you already saw. This usage is deprecated, but to be complete here is an example:
5> Append = {lists, append}. {lists,append} 6> Append([1, 2], [3, 4, 5]). [1,2,3,4,5]
As you already noted Fun in Erlang is “richer” than lambda in Python.
Built-in functions, BIFs
Both Erlang and Python provide built-in functions that are always available and do not require explicit usage of module name to use them.
Python:
Read more about builtins in library reference.
In Python shell you can inspect in addition contents of __builtins__ module if you need:
>>> import pprint >>> pprint.pprint(dir(__builtins__)) ['ArithmeticError', 'AssertionError', ... 'type', 'unichr', 'unicode', 'vars', 'xrange', 'zip']
Erlang:
Most of the built-in functions belong to module erlang and they are auto-imported.
To get list of all BIFs with description read man erlang.
In Eshell you can get list of functions that belong to erlang module with m(Module). command:
1> m(erlang). Module erlang compiled: No compile time info available Compiler options: [] Object file: preloaded Exports: ‘!’/2 list_to_existing_atom/1 ‘$erase’/1 list_to_float/1 ‘$erase’/0 list_to_integer/2 ‘$get’/1 list_to_integer/1 ‘$get’/0 list_to_pid/1 ‘$get_keys’/1 list_to_tuple/1 ‘$put’/2 load_module/2 ‘*’/2 loaded/0 ‘+’/1 localtime/0 ‘+’/2 localtime_to_universaltime/1 ‘++’/2 localtime_to_universaltime/2 … ok
Take into account though that not all functions in above list that you’ll see are auto-imported and may require usage of module: prefix. Again, for more information read man erlang (either in command line with erl -man erlang or if you use Emacs take a look at Erlang man pages in Emacs)
Macros
Python does not have them, Erlang does. In Erlang they are not as powerful as in Common Lisp, but more like in C.
They are defined in following form:
-define(Const, Replacement). -define(Func(Var1,…,VarN), Replacement).
Whenever erlang preprocessor encounters expression of form ?MacroName it expands corresponding macro.
Define utils.erl:
-module(utils). -export([foo/0]). -define(TIMEOUT, 100). -define(FUNCMACRO(X, Y), {X, x, Y, y}). foo() -> io:format(“TIMEOUT = ~p~n”, [?TIMEOUT]), ?FUNCMACRO(3, 7).
Compile and run:
1> utils:foo(). TIMEOUT = 100 {3,x,7,y}
There are also some predefined macros:
-
?MODULE - The name of the current module.
-
?MODULE_STRING. - The name of the current module as a string.
-
?FILE. - The file name of the current module.
-
?LINE. - The current line number.
-
?MACHINE. - The machine name,
'BEAM'.
Having ?MODULE we can rewrite earlier example with Fun as:
-module(mymath). -export([test1/0, test2/0, test3/0, double/1]). test1() -> lists:map(fun(X) -> X * 2 end, [1, 2, 3]). test2() -> lists:map(fun double_local/1, [1, 2, 3]). %% test3() -> lists:map(fun mymath:double/1, [1, 2, 3]). test3() -> lists:map(fun ?MODULE:double/1, [1, 2, 3]). %% helper function, not exported double_local(X) -> X * 2. %% helper function, exported double(X) -> X * 2.
And as usual compile and run to see that result is the same:
1> c(”/home/alienoid/dev/erlang/mymath”, [{outdir, "/home/alienoid/dev/erlang/"}]). {ok,mymath} 2> mymath:test1(). [2,4,6] 3> mymath:test2(). [2,4,6] 4> mymath:test3(). [2,4,6]
In addition there is also flow control in macros. Some of corresponding macro directives are:
- ifdef(Macro). - Evaluate the following lines only if Macro is defined.
- else. - It’s used after ifdef or ifndef
- endif. - Marks the end of ifdef or ifndef directive
Example in utils.erl:
-module(utils). -export([foo/0]). -ifdef(debug). -define(LOG(Msg), io:format(“{~p:~p}: ~p~n”, [?MODULE, ?LINE, Msg])). -else. -define(LOG(Msg), true). -endif. foo() -> ?LOG(“Debug is enabled”).
To turn LOG macro on debug should be defined. This can be achieved from command line with erlc -Ddebug utils.erl or from Eshell using c/2 function:
1> c(utils, {d, debug}). {ok,utils} 2> utils:foo(). {utils:11}: “Debug is enabled” ok
To turn LOG macro off debug should be omitted:
3> c(utils). {ok,utils} 4> utils:foo(). true
To be continued.