Spawning functions without exporting them - erlang

I am new to erlang and have a bit of a headache with the following scenario:
Take this code:
-module (so).
-export ( [foo/0] ).
bar () ->
receive
die -> ok;
Msg -> io:format ("I say ~p.~n", [Msg] )
end.
bar (Name) ->
receive
die -> ok;
Msg -> io:format ("~s says ~p.~n", [Name, Msg] )
end.
foo () ->
Bar = spawn (fun bar/0),
Bar ! "Hello",
Bar ! die,
Baz = spawn (?MODULE, bar, ["Alice"] ), % bar/1 not exported
Baz ! "Hello",
Baz ! die.
The process spawned with spawn/1 works fine, but the second process spawned with spawn/3 fails as expected because I haven't exported bar/1 and hence I get {undef,[{so,bar,["Alice"]}]}. Both spawn functions that take a parameter list (spawn/3 and spawn/4) also take a module name. But I don't want to export bar/1, because it is only used internally and never needs to be invoked from outside the module.
How can I spawn a function with arguments without exporting it?

You can just put the call inside another fun like this:
spawn(fun() -> bar("alice") end)

Related

Erlang: Chat server crashing on startup

I got a set of tests that the program should pass and all the local tests works just fine with my server it's when I try to run the remote tests that the server crashes.
The crash message is the following:
=ERROR REPORT==== 23-Jul-2015::23:59:17 === Error in process <0.39.0> on
node 'nodeS#127.0.0.1' with exit value:
{undef,[{genserver,start,[server, {server_st,[],[]},#Fun<server.loop.2>],[]}]}
My start-up function looks as following:
loop(St, {From, Nick, connection_wanted}) ->
case lists:keymember(Nick, 2, St#server_st.users) of
false -> {ok, St#server_st{users = St#server_st.users ++ [{From, Nick}]}};
true -> {{user_already_connected, St}, St}
end;
With the record "server_st" is defined as:
-record(server_st, {users = [], channels = []}).
Finally the genserver start&loop function is:
start(Name, State, F) ->
Pid = spawn(fun() -> loop(State, F) end),
register(Name, Pid),
Pid.
loop(State, F) ->
receive
{request, From, Ref, Data} ->
case catch(F(State, Data)) of
{'EXIT', Reason} ->
From!{exit, Ref, Reason},
loop(State, F);
{R, NewState} ->
From!{result, Ref, R},
loop(NewState, F)
end;
{update, From, Ref, NewF} ->
From ! {ok, Ref},
loop(State, NewF);
stop ->
true
end.
Then genserver functions I'm not allowed to change. If needed I can post the whole testsuite too.
Edit
Digging a bit further into the test cases and I'm unsure if it really is the server that's causing the issue, my remote connect function looks as following:
loop(St, {connect, {_Server, _Machine}}) ->
ServerPID = {list_to_atom(_Server), list_to_atom(_Machine)},
case genserver:request(ServerPID, {self(), St#cl_st.nick, connection_wanted}) of
ok -> {ok, St#cl_st{connected_to = ServerPID}};
_ -> {{error, user_already_connected, "A user with the nick " ++ St#cl_st.nick ++ "is already connected to" ++ _Server}, St}
end;
Edit 2
Found the specific row inside the testsuite that's causing the error:
-define(HOST, '127.0.0.1').
new_client(Nick, GUIName) ->
ClientName = test_client:find_unique_name("client_"),
ClientAtom = list_to_atom(ClientName),
% Row below is causing the error
Result = slave:start(?HOST, ClientAtom),
assert_ok("start client node "++ClientName, element(1,Result)),
ClientNode = element(2,Result),
InitState = client:initial_state(Nick, GUIName),
Result2 = spawn(ClientNode, genserver, start, [ClientAtom, InitState, fun client:loop/2]),
assert("client startup "++ClientName, is_pid(Result2)),
{Nick, ClientAtom, ClientNode}.
Your function genserver:start/3 is most probably not exported or module genserver is not available at the node where you run code which calls it.
Solved it, was in a completely unrelated part where the client is communicating with other users. Still used the whereis command to locate other users from an older version of the program.

Writing and compiling custom behaviours in Erlang

I'm trying to write and compile a custom behaviour in Erlang.
I cannot find any clear documentation on how to compile this behaviour.
-module(bla).
-export([start_link/0,behaviour_info/1]).
behaviour_info(callbacks)->
[{init,1}];
behaviour_info(_Other)->
undefined.
%% -callback init(Args :: term()) ->
%% {ok, State :: term()} | {ok, State :: term(), timeout()} |
%% {stop, Reason :: term()} | ignore.
start_link()->
init([]).
my command for comiling is :
erlc.exe .\src\bla.erl
resulting:
bla.erl:24: function init/1 undefined
Anyone an idea on writing and compiling behaviours in erlang, please? any links?
Defining behaviour callbacks results in an obligation towards your implementation of callback module. In erlang, modules are just function containers, not classes nor interfaces. Behaviours are based on runtime module name resolution Mod:fun(). OTP gen_server (check it) keeps its callback module name after you pass it in: gen_server:start_link(CallbackModuleName, Args, Opts) and here is code for applying callback init/1:
init_it(Starter, Parent, Name0, Mod, Args, Options) ->
Name = name(Name0),
Debug = debug_options(Name, Options),
case catch Mod:init(Args) of
{ok, State} ->
proc_lib:init_ack(Starter, {ok, self()}),
loop(Parent, Name, State, Mod, infinity, Debug);
{ok, State, Timeout} ->
proc_lib:init_ack(Starter, {ok, self()}),
loop(Parent, Name, State, Mod, Timeout, Debug);
...
It's applying init/1 of passed callback module kept in Mod parameter, gets its last value, do what you want and keep going (or not, depends on that last value).
Assume we have module bla_impl which looks like this:
-module(bla_impl).
-behaviour(bla).
-export([init/1, start_link/0]).
start_link() -> bla:start_link(?MODULE). %% macro ?MODULE is resolved to bla_impl
init(Args) -> ... .
And now you need to say in bla which module you use by:
-module(bla).
-export([start_link/1]).
start_link(Mod) -> Mod:init([]).
or maybe better solution is to read it from configuration:
-module(bla).
-export([start_link/0]).
start_link() ->
Mod = application:get_env(bla_app, callback_module),
Mod:init([]),
...
There is many ways for doing so.
As you see there is no magic here. This would work even without -behaviour(bla) nor specified callback with -callback. This is just an information for compiler, tools and documentation.
From erlang documentation: Behaviours
And btw. start_link function should spawn another process and link to it.
start_link(Mod) ->
spawn_link(Mod, init, [[]]).

Erlang basic general server debugger output interpretation

I'm having some trouble with an Erlang module. Here is the one that I wrote:
-module(basic_gen_server).
-export([start/1, call/2, cast/2]).
start(Module) ->
register(server, spawn(basic_gen_server,gen_server_loop,[Module, Module:init()])), server.
call(Pid,Request) ->
Pid ! {call, self(), Request},
receive
Reply -> Reply
end.
cast(Pid,Request) ->
Pid ! {cast, self(), Request},
receive
_ -> ok
end.
gen_server_loop(Module, CurrentState) ->
io:fwrite("gen_server_loop~n", []),
receive
{call, CallPid, Request} ->
{reply, Reply, NewState} = Module:handle_call(Request,self(),CurrentState),
CallPid ! Reply,
gen_server_loop(Module, NewState);
{cast, CastPid, Request} ->
{noReply, NewState} = Module:handle_cast(Request, CurrentState),
CastPid ! noReply,
gen_server_loop(Module, NewState)
end.
And here is the callback module that was defined:
% Written by Caleb Helbling
% Last updated Oct 10, 2014
-module(name_server).
-export([init/0, add/3, whereis/2, handle_cast/2,
handle_call/3, handle_swap_code/1]).
%% client routines
add(ServerPid, Person, Place) ->
basic_gen_server:cast(ServerPid, {add, Person, Place}).
whereis(ServerPid, Person) ->
basic_gen_server:call(ServerPid, {whereis, Person}).
%% callback routines
init() ->
maps:new().
handle_cast({add, Person, Place}, State) ->
NewState = maps:put(Person, Place, State),
{noreply, NewState}.
handle_call({whereis, Person}, _From, State) ->
Reply = case maps:find(Person, State) of
{ok, Place} -> Place;
error -> error
end,
NewState = State,
{reply, Reply, NewState}.
handle_swap_code(State) ->
{ok, State}.
Upon trying to initialize the server with the following command:
MyServer = basic_gen_server:start(name_server).
I get the following debug output:
=ERROR REPORT==== 29-Oct-2014::12:41:42 ===
Error in process <0.70.0> with exit value: {undef,[{basic_gen_server,gen_server_loop,[name_server,#{}],[]}]}
Conceptually, I understand the notion of making serial code into a basic server system, but I believe that I have a syntax error that I haven't been able to find using either syntax highlighting or Google. Thanks in advance for the help!
Function gen_server_loop is not exported. So you can not call basic_gen_server:gen_server_loop(Module, Module:init()), which is what is happening inside spawn(basic_gen_server,gen_server_loop,[Module, Module:init()]).
If you read your error message it tells you that function you are trying to call in undefined (trougn undef atom). Function being {basic_gen_server,gen_server_loop,[name_server,#{}],[]}, or where you have {Module, Function, ListOfArgs, ...}. You always should check that
there are no types module or function name
function arity match number of arguments in call (List in error message)
function is exported
All local calls (like loop(SomeArgs), without module specified) will not compile if function is not defined. And you can do local call dynamically (FuntionName(SomeArgs) again without module name).
EDIT after comment about need of local calls.
You actually could use lambda for this. There is spawn/1 funciton, which takes lambda (or fun if you like), so you can call spawn( fun local_functino/0).. Only issue with that is fact that your fun can not take any arguments, but there is a way around it, with use of closures.
spawn(fun () ->
gen_server_loop(Module, Module:init())
end).
And gen_serve_loop stays local call.

Rebar distributed tests

I have some issue with distributed tests under rebar.
Rebar starts node with name nonode#nohost. After it I call help function make_distrib which provide normal node name and starts distribution work.
After starting the slave node I couldn't sent to it any lambda. I had error:
=ERROR REPORT==== 27-Jul-2013::22:48:02 ===
Error in process on node 'test1#just' with exit value: {{badfun,#Fun<msg_proxy_tests.2.117197241>},[{error_handler,undefined_lambda,3,[{file,"error_handler.erl"},{line,64}]}]}
But! If I run it test with simple way - all works good:
$erl
1> c(msg_proxy_tests).
{ok,msg_proxy_tests}
2> eunit:test({module, msg_proxy_tests},[verbose]).
======================== EUnit ========================
module 'msg_proxy_tests'msg_proxy_tests: distrib_mode_test_ (distribute mode test for nodes)...
msg_proxy_tests.erl:14:<0.48.0>: nodes ( ) = [test1#just]
msg_proxy_tests.erl:15:<9999.39.0>: node ( ) = test1#just
msg_proxy_tests.erl:17:<0.48.0>: nodes ( ) = [test1#just]
[0.238 s] ok
How can I fix this?
Module source:
-module(msg_proxy_tests).
-include_lib("eunit/include/eunit.hrl").
distrib_mode_test_()->
{"distribute mode test for nodes", timeout, 60,
fun() ->
{ok, Host} = inet:gethostname(),
make_distrib("tests#"++Host, shortnames),
slave:start(Host, test1),
?debugVal(nodes()),
spawn(list_to_atom("test1#"++Host), fun()-> ?debugVal(node()) end),
timer:sleep(100),
?debugVal(nodes()),
stop_distrib(),
ok
end}.
-spec make_distrib( NodeName::string()|atom(), NodeType::shortnames | longnames) ->
{ok, ActualNodeName::atom} | {error, Reason::term()}.
make_distrib(NodeName, NodeType) when is_list(NodeName) ->
make_distrib(erlang:list_to_atom(NodeName), NodeType);
make_distrib(NodeName, NodeType) ->
case node() of
'nonode#nohost' ->
[] = os:cmd("epmd -daemon"),
case net_kernel:start([NodeName, NodeType]) of
{ok, _Pid} -> node()
end;
CurrNode -> CurrNode
end.
stop_distrib()->
net_kernel:stop().
That is because of module hash on both nodes is different. That happens because eunit compiles code before it run and module contains -ifdef(TEST) macroses, which will definitely change it's hash.
Thats why anonymous function #Fun<msg_proxy_tests.2.117197241 could not be called and error occurred. (look at function name and you will notice funny numbers, this is module hash, it will be different on both nodes).
If you want to avoid this, you should call funs by it fully qualified name: module:fun_name/arity.

How to show Erlang call stack?

I need to debug some module in foreign system. The module has public function foo() - how can I know place (module and function name)
from which foo() given module was called? I mean stack of calls.
I cannot stop system, all work I can do by reload this module (but with some debug info).
-module(given).
-export(foo/0).
foo() ->
%% here is my debug - and
%% i need here(!) known about unknown_module:unknown_foo!
ok.
---
-module(unknown_module).
..
unknown_foo() ->
given:foo(). %% see above
Here's a simple trick:
Trace = try throw(42) catch 42 -> erlang:get_stacktrace() end,
erlang:display(Trace)
This might work:
where_am_i() ->
try throw(a)
catch throw:a:Stacktrace ->
Stacktrace
end.
Except that it doesn't work for tail calls. For example, given these two functions:
foo() ->
where_am_i().
bar() ->
X = where_am_i(),
{ok, X}.
I get these results:
4> foo:foo().
[{foo,where_am_i,0},
{erl_eval,do_apply,5},
{shell,exprs,6},
{shell,eval_exprs,6},
{shell,eval_loop,3}]
5> foo:bar().
{ok,[{foo,where_am_i,0},
{foo,bar,0},
{erl_eval,do_apply,5},
{shell,exprs,6},
{shell,eval_exprs,6},
{shell,eval_loop,3}]}
That is, I can only see bar, since foo's call frame has been left already when where_am_i is called.
io:format("~s~n", [element(2, process_info(self(), backtrace))]).
self() can be replaced by any other pid (rpc:pinfo should even work with remote procs). This helps if you cannot even modify the source or beam.
Here is my code for doing this:
format_stack_entry(S) ->
{Module,Fun,Arity,[{file,File},{line,Line}]}=S,
io_lib:format("{~p,~p,~p,[{file,~p},{line,~p]}",[Module,Fun,Arity,File,Line]).
stacktop([Top|_]) ->
Top.
ancestor(N) ->
{_,Stacktrace}=erlang:process_info(self(),current_stacktrace),
ancestor(N+1,Stacktrace).
ancestor(1,S) ->
format_stack_entry(stacktop(S));
ancestor(N,[_|T]) ->
ancestor(N-1,T).
info(Format) -> io:format(lists:concat([ancestor(2),Format,"\r"])).
info(Format,Args) -> io:format(lists:concat([ancestor(2),Format,"\r"]),Args).
Lists is a custom module in the system. Use your foo module instead.

Resources