Tuesday, November 17, 2009

Reading Notes #2: Passing functions as arguments to higher order functions

Does Erlang allow you to pass functions (not "funs" or anonymous functions) as arguments to higher order functions? I was under the impression that it doesn't. To find out I tweeted a question about the same. After looking at the answers I decided to rephrase the question with the help of a concrete example. Here it goes:

Step 1: Define a function add_one in a module named test.
  1. % test.erl  
  2. -module(test).  
  3. -export([add_one/1]).  
  4.   
  5. add_one(X) -> X + 1.  

Step 2: Start the erlang shell and try to use this function.
  1. 1> c(test).  
  2. {ok,test}  
  3. 2> test:add_one(2).  
  4. 3  
  5. 3> lists:map(test:add_one, [1, 2, 3]).  
  6. * 1: illegal expression  
  7. 4> lists:map(fun(X) -> test:add_one(Xend, [1, 2, 3]).  
  8. [2,3,4]  
  9. 5> lists:map(fun(X) -> X + 1 end, [1, 2, 3]).  
  10. [2,3,4]  
  11. 6>   

As you can see I have tried three different combinations. Line #3 shows a call to the higher order function lists:map passing in the qualified name of the add_one function as first argument. The shell throws an error.

In Line #4 I wrap test:add_one inside a fun and it works.

In Line #5 I replicate the code of add_one inside an anonymous fun and everything works fine.

Contrast this with say, Python.

Definition:
  1. def add_one(x): return x + 1  

Usage:
  1. >>> from test import add_one  
  2. >>> map(add_one, [1, 2, 3])  
  3. [2, 3, 4]  
  4. >>>   

Now I'll repeat the question: Does Erlang allow you to pass functions (not "funs" or anonymous functions) as arguments to higher order functions? Or am I missing something?

Please share your thoughts in the comments.

Update #1:
Got an answer. There is a way to do it. It still involves using the fun keyword.
  1. 1> c(test).  
  2. {ok,test}  
  3. 2> lists:map(fun test:add_one/1, [1, 2, 3]).  
  4. [2,3,4]  
  5. 3>   

Update #2:
Got another answer which does *not* use fun. The syntax is just a little different. Thanks to Justin Sheehy.
  1. 1> c(test).  
  2. {ok,test}  
  3. 2> lists:map({test,add_one}, [1, 2, 3]).  
  4. [2,3,4]  
  5. 3>