Erlang for Python programmers: Part IV

by Ruslan Spivak on October 2, 2007

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:

  1. fun FunctionName/Arity (FunctionName should point to local function defined in the same module with our fun)
  2. 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.

If you enjoyed this post why not subscribe via email or my RSS feed and get the latest updates immediately. You can also follow me on GitHub or Twitter.

{ 1 comment… read it below or add one }

Angela Jones March 6, 2010 at 6:33 PM

This is great! Thanks for your article. I am new at python and this will help a lot.

Reply

Speak your mind

Previous post:

Next post: