In Erlang, how do you invoke a function dynamically? - erlang

I want to call xyz with the name of a function to be invoked.
-module(sample).
-export([xyz/1]).
xyz(Name) -> Name().
p() -> "you called p".
g() -> "you called g".
But I get the following error:
1> c(sample.erl).
./sample.erl:6: Warning: function p/0 is unused
./sample.erl:7: Warning: function g/0 is unused
{ok,sample}
2> sample:xyz('p').
** exception error: bad function p
in function sample:xyz/1
3>

It is correct that you have to export p and g. You can then use apply/3 to call it.
erlang:apply(sample, p, []).
Only fun-values are usable with the Fun(...) syntax. You are passing in an atom-value. An atom is a 'bad function' as the error message go. You could do something similar to
xyz(p) -> fun p/0;
xyz(g) -> fun g/0.
Then go ahead and call
Fun = xyz(p),
Fun()

-module(sample).
-export([xyz/1, p/0, g/0]).
xyz(Name) -> ?MODULE:Name().
p() -> "you called p".
g() -> "you called g".
1> sample:xyz(p).
"you called p"

Pattern match is the idiom to use:
-module(sample).
-export([xyz/1]).
xyz(p) -> p();
xyz(q) -> g().
p() -> "you called p".
g() -> "you called g".
If you want to be dynamic you can use a gen_event server.
Essentially what this is is a server that holds a state which consists of key/function pair like so:
[{p, #func1},
{g, #func2},
{..., ...},
...]
You can then essentially bind events to functions. (there is, needless to say, a bit more to it than that.

The easiest way to do is to try exporting p and g along with xyz.
-export([xyz/1, p/0,g/0]).
After exporting the function p and g can be called as follows :
1> sample:xyz(fun sample:p/0).
"you called p"
2> sample:xyz(fun sample:g/0).
"you called g"

Another way to look at it is that (depending on the problem you're solving) dynamic calls to functions isn't necessarily the right approach. Given that processes and message passing are the way you organize your code in Erlang since it's a "concurrency oriented language", maybe you could just use message passing with a selective receive rather than mimicking idioms of a sequential language? Send a message for what you want and get the custom reply based on that. It's about the result of each function, not the function itself, after all. (Plus there's the flexibility and scalability of message passing, etc.)
Although processes aren't totally free compared to calling from a library module, Erlang-level processes are dirt cheap (especially if the message communication is within the same node). They're not OS-level processes. The overhead would be comparable (or better) to dynamic function calls and object instantiation in heavier scripting languages.

Related

What's the point of meck:validate?

As a newcomer to meck, I've been putting together a test that shows the various features. I cannot, however, understand why a developer might call meck:validate. Here's my example:
-module(meck_demo).
-include_lib("eunit/include/eunit.hrl").
validate_is_of_limited_use_test_() ->
{ foreach, fun setup_mock/0, fun cleanup_mock/1,
[fun validate_does_not_fail_if_a_function_is_not_called/0,
fun validate_does_not_fail_if_a_function_is_called_with_wrong_arity/0,
fun validate_does_not_fail_if_an_undefined_function_is_called/0,
fun validate_does_fail_if_a_function_was_called_with_wrong_argument_types/0,
fun validate_does_fail_if_expectation_throws_an_unexpected_exception/0 ]}.
validate_does_not_fail_if_a_function_is_not_called() ->
meck:expect(womble, name, fun() -> "Wellington" end),
?assert(meck:validate(womble)).
validate_does_not_fail_if_a_function_is_called_with_wrong_arity() ->
meck:expect(womble, name, fun() -> "Madame Cholet" end),
?assertError(undef, womble:name(unexpected_arg)),
?assert(meck:validate(womble)).
validate_does_not_fail_if_an_undefined_function_is_called() ->
?assertError(undef, womble:fly()),
?assert(meck:validate(womble)).
validate_does_fail_if_a_function_was_called_with_wrong_argument_types() ->
meck:expect(womble, jump, fun(Height) when Height < 1 ->
ok
end),
?assertError(function_clause, womble:jump(999)),
?assertNot(meck:validate(womble)).
validate_does_fail_if_expectation_throws_an_unexpected_exception() ->
meck:expect(womble, jump, fun(Height) -> 42 = Height end),
?assertError({badmatch, 999}, womble:jump(999)),
?assertNot(meck:validate(womble)).
setup_mock() ->
meck:new(womble, [non_strict]).
cleanup_mock(_SetupResult) ->
meck:unload(womble).
What am I missing?
-- Updated to reflect the cases that Adam explains can be caught
You managed to hit just about every case not covered by validate (added better documentation in 10c5063).
Validate can detect:
When a function was called with the wrong argument types (function_clause)
When an exception was thrown
When an exception was thrown and expected (via meck:exception/2), which still results in true being return from meck:validate/1
Validate cannot detect:
When you didn't call a function
When you called a function with the wrong number of arguments
If you called an undefined function
The reason it cannot detect these cases is because of how Meck is implemented. Meck replaces the module with a mock and a process that maintains the mock. Everything Meck gets goes through that mock module. Meck does not insert itself at the caller level (i.e. in your module or in your test case), so it cannot know that you failed to call a module. All of the failures in your test case above never reaches the mock module in the first place.

Can you use a list (or other collection) to specify what messages to receive in Erlang?

E.g. suppose I have a list that looks something roughly like this:
Handlers = [{foo, FooHandler}, {bar, BarHandler} | Etc()]
The best that I can come up with is this:
receive
Message ->
Handler = find_matching_handler(Message, Handlers),
Handler(Message)
end
The problem with this is that if Message does not match anything in Handlers, it's too late: I've taken it out of the mailbox.
I guess if there's a way to put a message back into the mailbox (into the save queue) without reordering, then that would take care of it. Simply resending to self() would reorder. It would also not restart the receive, and even if it did, you might get stuck in a spin loop until a message of interest arrives. Is there a way to put a message into the mailbox's save queue?
Another near solution that I thought of was to use match guard, but IIUC, you can only use BIFs in guards, which seems to preclude using find_matching_handler (unless there is a BIF for that).
Another near solution: map matching:
receive
M when Handlers#{M := Handler} -> Handler(M) % booyah?
end
Alas, I have not found an incantation that satisfies Erlang...
Match on the message:
loop() ->
receive
{foo, Data} ->
handle_foo(Data),
loop();
{bar, Data} ->
handle_bar(Data),
loop()
end.
This is the basic way of distinguishing between message forms.
You can also be less direct and match in a function head you pass all messages to:
loop() ->
receive
Message ->
handle_message(Message),
loop()
end.
handle_message({foo, Data}) ->
foo(Data),
ok;
handle_message({bar, Data}) ->
bar(Data),
ok.
A combination of the first and second forms is sort of the way gen_server type callback modules are structured in OTP. The message handlers receive a slightly more complex set of arguments and exist in their own module (the part you write), and the actual receive occurs in the generic gen_server module.
You can use a selective receive pattern to periodcally scan the mailbox for handler messages. Something like this:
check_msg_handlers(Handlers) ->
[check_handler(X) || X <- Handlers],
timer:sleep(500),
check_msg_handlers(Handlers).
check_handler(Handler) ->
receive
{_Handler={M,F}, Msg} ->
M:F(Msg)
after
0 ->
no_msg
end.
Note the receive X -> Y after -> N no_msg end, this is the selective receive. When using a timeout of N=0 it effectively becomes a scan of the mailbox to see if the X message is present or not, i.e. it becomes a non-blocking receive. The order of the messages is preserved after the scan as required in your case.
The LYSE chapter More On Multiprocessing has a section on selective receives that is very good.

How to mock objects in Erlang using Meck?

Okay, I'm using Meck and I'm lost. My first language (that I've been writing for about 7 months) is Ruby, so I can't seem to wrap my brain around Meck mocking yet. I do get Ruby mocking though. Hoping someone can help me. Also, I've only been writing Erlang for a week.
Updated Code (but mocking still isn't working)...
I have a Erlang console_io prompter module that looks like this:
-module(prompter).
-export([prompt/1, guess/0]).
prompt(Message) ->
console_io:gets(Message).
gets() ->
{_, [Input]} = io:fread("Enter: ", "~s"),
Input.
guess() ->
Guess_Input = gets(),
Guess_List = convert_guess_to_list(Guess_Input).
convert_guess_to_list(Guess_Input) ->
re:split(Guess_Input, "", [{return, list}, trim]).
My test now looks like this:
-module(prompter_test).
-include_lib("eunit/include/eunit.hrl").
guess_1_test() ->
meck:new(prompter),
meck:expect(prompter, gets, fun() -> "aaaa" end),
?assertEqual(prompter:guess(), ["a","a","a","a"]),
?assert(meck:validate(prompter)),
meck:unload(prompter).
The error I'm getting is this:
Eshell V5.9.3.1 (abort with ^G)
1> prompter_test: guess_1_test (module 'prompter_test')...*failed*
in function prompter:guess/0
called as guess()
in call from prompter_test:guess_1_test/0 (test/prompter_test.erl, line 10)
in call from prompter_test:guess_1_test/0
**error:undef
I want to mock (stub?) the gets function in my test so that gets will return "aaaa" and then when I assert on get_guess() it should equal ["a", "a", "a", "a"].
How do I do this?
There are two problems:
The prompter module has two exported functions, but you only mock one of them (gets) with meck:expect. By default, Meck creates a new module that only contains the functions that you explicitly mock. You can change that by using the passthrough option:
meck:new(prompter, [passthrough]),
When you mock the gets function, all module-prefixed calls (i.e. prompter:gets()) are intercepted, but Meck has no way (yet?) of intercepting internal calls (e.g. the gets() call in the guess function), so you would still get the unmocked version of the function. There is no completely satisfactory way to avoid this. You could change the call in guess to prompter:gets(), or you could move gets into a separate module and mock that.
The first line says to create a new mocked module, my_library_module:
meck:new(my_library_module),
Next, we mock the function fib in my_library_module to return 21 when 8 is passed in:
meck:expect(my_library_module, fib, fun(8) -> 21 end),
We have some eunit assertions to test our mocked function. The code_under_test:run call is what you want to replace with the the function using your mocked module, and the 21 is the result you are expecting from the function call:
?assertEqual(21, code_under_test:run(fib, 8)), % Uses my_library_module
?assert(meck:validate(my_library_module)),
Then we unload the mocked module:
meck:unload(my_library_module).
If you wanted to write the same test for your module, you could write:
my_test() ->
meck:new(console_io),
meck:expect(console_io, gets, fun() -> "aaaa" end),
?assertEqual(["a", "a", "a", "a"], console_io:get_guess()), % Uses console_io
?assert(meck:validate(console_io)),
meck:unload(console_io).

Unable to use Erlang/ets in receive block

I am trying to use Erlang/ets to store/update various informations by pattern matching received data. Here is the code
start() ->
S = ets:new(test,[]),
register(proc,spawn(fun() -> receive_data(S) end)).
receive_data(S) ->
receive
{see,A} -> ets:insert(S,{cycle,A}) ;
[[f,c],Fcd,Fca,_,_] -> ets:insert(S,{flag_c,Fcd,Fca});
[[b],Bd,Ba,_,_] -> ets:insert(S,{ball,Bd,Ba})
end,
receive_data(S).
Here A is cycle number, [f,c] is center flag , [b] is ball and Fcd,Fca, Bd, Ba are directions and angle of flag and ball from player.
Sender process is sending these informations. Here, pattern matching is working correctly which I checked by printing values of A, Fcd,Fca..etc. I believe there is something wrong with the use of Erlang/ets.
When I run this code I get error like this
Error in process <0.48.0> with exit value: {badarg,[{ets,insert,[16400,{cycle,7}]},{single,receive_data,1}]
Can anybody tell me what's wrong with this code and how to correct this problem?
The problem is that the owner of the ets-table is the process running the start/1 function and the default behavior for ets is to only allow the owner to write and other processes to read, aka protected. Two solutions:
Create the ets table as public
S = ets:new(test,[public]).
Set the owner to your newly created process
Pid = spawn(fun() -> receive_data(S) end,
ets:give_away(test, Pid, gift)
register(proc,Pid)
Documentation for give_away/3

Declaration of ETS in Erlang

The following code gives me an error: "syntax error before: Some_ets"
-module(tut).
-export([incr/1]).
Some_ets = ets:new(?MODULE, [bag]).
incr(X) ->
X+1.
But I am able to declare the ETS within a function, like:
-module(tut).
-export([incr/1]).
incr(X) ->
Some_ets = ets:new(?MODULE, [bag]),
X+1.
Can't I declare a ETS outside a function?
No - unlike other languages there isn't a concept of static initialization - there's no appropriate time for an Erlang system to execute that piece of code.
Erlang does have the concept of a parameterized module however, and that may be what you're after. Have a look here http://www.lshift.net/blog/2008/05/18/late-binding-with-erlang which is a good write up of that - it would allow you to instantiate an "instance" of your tut module bound to a given ets table and save passing around that handle explicitly in your module function calls.
Or if you are into OTP you could have the handle to the ets table passed around in the state variable:
init(_) ->
Some_ets = ets:new(?MODULE, [bag]),
{ok, Some_ets}.
and then use it in your handle_call methods:
get_ets_handle() ->
gen_server:call(?MODULE, {getETSHandle}, infinity).
handle_call({getETSHandle}, _From, Some_ets) ->
{reply, Some_ets, Some_ets}.
You can't do variable assignments like that in a module. See here.

Resources