Related
There is a simple implementation of the factorial function in an 'escript' in the Erlang docs. The factorial function is given as:
fac(0) -> 1;
fac(N) -> N * fac(N-1).
That's all fine, I can get this to work, no problem.
I would however like to know how I can implement this same, simple factorial function in an 'OTP way' using rebar3?
Just to be clear, my questions are:
Where does the code go?
How would I call it from the shell?
Could I also run it from the command line like I do via the escript example?
FYI, I have gotten started with rebar3. Here is where I am at:
rebar3 new app factorial
creates a few files but specifically the code is in 3 files in a src directory. I can see that a supervisor is being used, seems fine.
I can interact with this project from the shell:
$ rebar3 shell
1> application:which_applications().
[{factorial,"An OTP application","0.1.0"},
{inets,"INETS CXC 138 49","7.0.3"},
{ssl,"Erlang/OTP SSL application","9.1.1"},
{public_key,"Public key infrastructure","1.6.4"},
{asn1,"The Erlang ASN1 compiler version 5.0.8","5.0.8"},
{crypto,"CRYPTO","4.4"},
{stdlib,"ERTS CXC 138 10","3.7"},
{kernel,"ERTS CXC 138 10","6.2"}]
2> application:stop(factorial).
=INFO REPORT==== 21-Jan-2019::12:42:07.484244 ===
application: factorial
exited: stopped
type: temporary
ok
3> application:start(factorial).
ok
Where does the code go?
To 'call code in the OTP way', you can put it behind a gen_server.
For this simple factorial function, I added a new file factorial.erl within the src directory which is pretty much a standard gen_server skeleton with my factorial function as one of the callbacks:
% factorial.erl
-module(factorial).
-behaviour(gen_server).
-export([start_link/0, stop/0, calc/1]).
<boilerplate gen_server stuff here, like init, etc.>
calc(N) ->
{ok, Result} = gen_server:call(?SERVER, {calc, N}),
{ok, Result}.
handle_call({calc, N}, _From, State) ->
Factorial = factorial(N),
Reply = {ok, Factorial},
{reply, Reply, State};
factorial(0) ->
1;
factorial(N) ->
N * factorial(N-1).
Since my rebar3 new app factorial created a supervisor, I modified the supervisor's init so that it calls my factorial module:
% factorial_sup.erl
<skeleton supervisor stuff here>
init([]) ->
Server = {factorial, {factorial, start_link, []},
permanent, 2000, worker, [factorial]},
Children = [Server],
RestartStrategy = {one_for_one, 0, 1},
{ok, {RestartStrategy, Children}}.
How do I call it from the shell?
$ rebar3 shell
<Enter>
1> factorial:calc(5).
{ok,120}
Since this is running under a supervisor, we can still stop and restart it:
2> application:stop(factorial).
=INFO REPORT==== 22-Jan-2019::13:31:29.243520 ===
application: factorial
exited: stopped
type: temporary
ok
3> factorial:calc(5).
** exception exit: {noproc,{gen_server,call,[factorial,{calc,5}]}}
in function gen_server:call/2 (gen_server.erl, line 215)
in call from factorial:calc/1 (/Users/robert/git/factorial/src/factorial.erl, line 32)
4> application:start(factorial).
ok
5> factorial:calc(5).
{ok,120}
How do I create an executable?
Work in progress :-).
working through Joe's book, got stuck on Chapter 12 exercise 1. That exercise is asking one to write a function start(AnAtom,Fun) that would register AnAtom as spawn(Fun). I've decided to try something seemingly easier - took the chapter's finished 'area_server' module, and modified it's start/0 function like this:
start() ->
Pid = spawn(ex1, loop, []),
io:format("Spawned ~p~n",[Pid]),
register(area, Pid).
so in place of a process executing the arbitrary Fun, I am registering the 'loop', which is a function in the area_server module doing all the work:
loop() ->
receive
{From, {rectangle, Width, Ht}} ->
io:format("Computing for rectangle...~n"),
From ! {self(), Width*Ht},
loop();
{From, {square, Side}} ->
io:format("Computing for square...~n"),
From ! {self(), Side*Side},
loop();
{From, Other} ->
io:format("lolwut?~n"),
From ! {self(), {error, Other}},
loop()
end.
It seems to be working just fine:
1> c("ex1.erl").
{ok,ex1}
2> ex1:start().
Spawned <0.68.0>
true
3>
3> area ! {self(), hi}.
lolwut?
{<0.61.0>,hi}
4> flush().
Shell got {<0.68.0>,{error,hi}}
ok
5> area ! {self(), {square, 7}}.
Computing for square...
{<0.61.0>,{square,7}}
6> flush().
Shell got {<0.68.0>,49}
ok
Thing went bad when I've tried to test that multiple processes can talk to the registered "server". (CTRL-G, s, c 2)
I'm in a new shell, running alongside the first - but the moment I send a message from this new shell to my 'area' registered process, something nasty happens - when querying process_info(whereis(area)), process moves from this state:
{current_function,{ex1,loop,0}},
{initial_call,{ex1,loop,0}},
to this one:
{current_function,{io,execute_request,2}},
{initial_call,{ex1,loop,0}},
while the message queue starts to grow, messages not getting processed. Hanging in module io, huh! Something is blocked on the io operations? Apparently the process is moved from my ex1:loop/0 into io:execute_request/2 (whatever that is)... are my silly prints causing the problem?
Your processes are doing what you expect with the exception of handling who has control over STDOUT at what moment. And yes, this can cause weird seeming behaviors in the shell.
So let's try something like this without any IO commands that are implied to go to STDOUT and see what happens. Below is a shell session where I define a loop that accumulates messages until I ask it to send me the messages it has accumulated. We can see from this example (which does not get hung up on who is allowed to talk to the single output resource) that the processes behave as expected.
One thing to take note of is that you do not need multiple shells to talk to or from multiple processes.
Note the return value of flush/0 in the shell -- it is a special shell command that dumps the shell's mailbox to STDOUT.
Eshell V9.0 (abort with ^G)
1> Loop =
1> fun L(History) ->
1> receive
1> halt ->
1> exit(normal);
1> {Sender, history} ->
1> Sender ! History,
1> L([]);
1> Message ->
1> NewHistory = [Message | History],
1> L(NewHistory)
1> end
1> end.
#Fun<erl_eval.30.87737649>
2> {Pid1, Ref1} = spawn_monitor(fun() -> Loop([]) end).
{<0.64.0>,#Ref<0.1663562856.2369257474.102541>}
3> {Pid2, Ref2} = spawn_monitor(fun() -> Loop([]) end).
{<0.66.0>,#Ref<0.1663562856.2369257474.102546>}
4> Pid1 ! "blah".
"blah"
5> Pid1 ! "blee".
"blee"
6> Pid1 ! {self(), history}.
{<0.61.0>,history}
7> flush().
Shell got ["blee","blah"]
ok
8> Pid1 ! "Message from shell 1".
"Message from shell 1"
9> Pid2 ! "Message from shell 1".
"Message from shell 1"
10>
User switch command
--> s
--> j
1 {shell,start,[init]}
2* {shell,start,[]}
--> c 2
Eshell V9.0 (abort with ^G)
1> Shell1_Pid1 = pid(0,64,0).
<0.64.0>
2> Shell1_Pid2 = pid(0,66,0).
<0.66.0>
3> Shell1_Pid1 ! "Message from shell 2".
"Message from shell 2"
4> Shell1_Pid2 ! "Another message from shell 2".
"Another message from shell 2"
5> Shell1_Pid1 ! {self(), history}.
{<0.77.0>,history}
6> flush().
Shell got ["Message from shell 2","Message from shell 1"]
ok
7>
User switch command
--> c 1
11> Pid2 ! {self(), history}.
{<0.61.0>,history}
12> flush().
Shell got ["Another message from shell 2","Message from shell 1"]
ok
I am new to Erlang and I am learning by building a really small chat program in pure Erlang.
I would like for clients to be able to connect to a server and then send messages to each other. But this is all done on a local machine and not over a network just for learning.
I have a list of all the clients that have connected to the server.
If client A sends a message to client B, I get the desired output in client A's terminal but I can't work out how to get the message from client A to be displayed on client B's terminal.
Or do I have to set each client with it's own mini server
-module(server).
-export([start/0]).
-export([server/1]).
-export([connect/0]).
-export([sendMessage/2]).
%%
%% The Server
%%
start() ->
EmptyList = [],
Pid = spawn(server, server, [EmptyList]),
register(chatServe, Pid).
server(ListOfClients) ->
receive
{Client, connect} ->
Client ! {chatServe, connected},
List = clientList(ListOfClients, Client),
server(List);
{Client, message, MessageBody} ->
List = ListOfClients,
lists:foreach(fun(X) -> X ! {chatServe, new_message, MessageBody} end, List),
Client ! {chatServe, received},
server(List)
end.
%%
%% The client will call rpc:call(server#local, server, connect, [])
%% to connect
%%
connect() ->
chatServe ! {self(), connect},
receive
{chatServe, connected} -> connected
end.
%%
%% The send message method takes two args
%%
%%
sendMessage(SendTo, MessageBody) ->
chatServe ! {self(), message, MessageBody},
receive
{chatServe, received} -> received
end.
receiveMessage(SendTo, SendFrom, MessageBody) ->
receive
{}
end.
%%
%% Some helper functions
%%
clientList(List, Client) when length(List) =:= 0 ->
io:format("List Size = 1~n"),
[Client];
clientList(List, Client) ->
io:format("List size = ~p~n", [length(List) + 1]),
[Client | List].
forwardMessage(SendTo, SentFrom, MessageBody, [H | T]) when H =:= SendTo ->
SendTo ! {SentFrom, message, MessageBody};
forwardMessage(SendTo, SentFrom, MessageBody, [H | T]) ->
forwardMessage(SendTo, SentFrom, MessageBody, T);
forwardMessage(SendTo, SentFrom, MessageBody, []) -> [].
In the terminal of the client I am calling
rpc:call(host, mod, function, args).
So my question is how can I get Client A to send a message to Client B through server C, with Client A displaying success, and Client B displaying the message that was sent?
Thanks in advance
In this example my server is an Erlang node and every client is an erlang node too.
Code:
-module(test).
-export([server_start/1, client_start/2]).
-export([server_new_message/2, client_new_message/2]).
server_start(ServerName) ->
{ok, _Pid} = net_kernel:start([ServerName, shortnames]),
erlang:register(server, erlang:self()),
io:format("Server '~p' started.~nMessages: ~n ~n", [erlang:node()]),
server_loop().
server_loop() ->
receive
{msg, Name, Text} ->
io:format("~p: ~p~n", [Name, Text]),
Receivers = lists:delete(Name, erlang:nodes()),
rpc:multicall(Receivers, ?MODULE, client_new_message, [Name, Text]),
server_loop()
end.
%% Server runs this function in client's node.
%% 'client' process in client's node will receive this message and print it
client_new_message(Name, Text) ->
client ! {msg, Name, Text}.
client_start(ServerName, ClientName) ->
{ok, _Pid} = net_kernel:start([ClientName, shortnames]),
pong = net_adm:ping(ServerName),
timer:sleep(1000), % wait for updating erlang:nodes()
Other = lists:delete(ServerName, erlang:nodes()),
io:format("Client '~p' connected to server '~p'.~nOnline users: ~p~n", [erlang:node(), ServerName, Other]),
erlang:register(client, spawn_link(fun print/0)),
client_loop(ServerName).
print() ->
receive
{msg, Name, Text} ->
io:format("~p: ~p~n", [Name, Text]),
print()
end.
client_loop(ServerName) ->
rpc:call(ServerName, ?MODULE, server_new_message, [erlang:node(), io:get_line(">>> ")]),
client_loop(ServerName).
%% Clients run this function in server's node
%% 'server' process will receive messages and print them and broadcast them
server_new_message(Name, Text) ->
server ! {msg, Name, Text}.
Run:
I open 3 Erlang shells.
In shell 1 i run server:
p#jahanbakhsh ~/Desktop $ erl
Erlang/OTP 19 [erts-8.2.2] [source-1ca84a4] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V8.2.2 (abort with ^G)
1> test:server_start(local_chat_server).
Server 'local_chat_server#jahanbakhsh' started.
Messages:
Server is waiting for messages.
In shell 2 i run client 1:
p#jahanbakhsh ~/Desktop $ erl
Erlang/OTP 19 [erts-8.2.2] [source-1ca84a4] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V8.2.2 (abort with ^G)
1> test:client_start('local_chat_server#jahanbakhsh', client_1).
Client 'client_1#jahanbakhsh' connected to server 'local_chat_server#jahanbakhsh'.
Online users: []
>>>
Now i can send message from this terminal, but wait.
I run client 2 in shell 3:
p#jahanbakhsh ~/Desktop $ erl
Erlang/OTP 19 [erts-8.2.2] [source-1ca84a4] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V8.2.2 (abort with ^G)
1> test:client_start('local_chat_server#jahanbakhsh', client_2).
Client 'client_2#jahanbakhsh' connected to server 'local_chat_server#jahanbakhsh'.
Online users: [client_1#jahanbakhsh]
>>>
I send a message ("Test message") from shell 2 or client 1.
In shell 1 or server i have:
client_1#jahanbakhsh: "Test message\n" - sent to [client_2#jahanbakhsh]
In shell 3 or client 2 i have:
client_1#jahanbakhsh: "Test message\n"
>>>
I'm a beginner at erlang programming. To understand hot code loading better, I used the example from Wikipedia (I added responses to the sending Pid for debugging):
%% A process whose only job is to keep a counter.
%% First version
-module(counter).
-export([start/0, codeswitch/2]).
start() -> loop(0).
loop(Sum) ->
receive
{increment, Count} ->
loop(Sum+Count);
%% modified code, which will be loaded:
% reset ->
% loop(0);
{counter, Pid} ->
Pid ! {counter, Sum},
loop(Sum);
{code_switch, Pid} ->
Pid ! {switch, Sum},
?MODULE:codeswitch(Pid, Sum)
% Force the use of 'codeswitch/2' from the latest MODULE version
end.
codeswitch(FromPid, Sum) ->
FromPid ! {switched, Sum},
loop(Sum).
All is good. I can load the module via c(counter). in the shell, spawn a new process via Pid = spawn(fun counter:start/0). and send messages to the spawned process. When I now add a new pattern to the receive expression reset -> loop(0) and reload the code via c(counter)., everything works as expected, new code is loaded, Sum keeps its incremented value etc.
But when I send the {code_switch, self()} message, Sum gets reset to 0 when loop(Sum) is called (FromPid ! {switched, Sum} in the call to FromPid ! {switched, Sum} still returns correct state).
What am I missing, why does my state go away after the first call to an codeswitched function?
Thanks for your help!
| 18 | Pid ! {counter, self()}.
{counter,<0.49.0>}
| 19 | flush().
Shell got {counter,6}
ok
| 20 | Pid ! {code_switch, self()}.
{code_switch,<0.49.0>}
| 21 | flush().
Shell got {switch,6}
Shell got {switched,6}
ok
| 22 | Pid ! {counter, self()}.
{counter,<0.49.0>}
| 23 | flush().
Shell got {counter,0}
ok
I put io:format("DebugInfo:~p~n", [Sum]) as the first expression in loop. Result is:
12> Pid ! {code_switch, self()}.
DebugInfo:3
{code_switch,<0.33.0>}
DebugInfo:0
13> flush().
Shell got {switch,3}
Shell got {switched,3}
ok
EDIT: I found that when I spawn the process via spawn/3, aka spawn(counter, start, [])., this works. When I spawn the process via spawn/1, aka spawn(fun counter:start/0), this doesn't
work. Is this expected behavior? What am I missing?
Documentation states for spawn/1:
Returns the process identifier of a new process started by the application of Fun to the empty list []. Otherwise works like spawn/3.
EDIT: .... Aaaand after trying to replicate this on an Ubuntu virtual machine (where it didn't happen), I am now also unable to reproduce this (and will test my memory for corruption now..)
This is not the behavior that I am seeing when testing your program:
25> LPid ! {counter, self()}.
{counter,<0.39.0>}
26> flush().
Shell got {counter,6}
ok
27> c(counter).
{ok,counter}
28> LPid ! {counter, self()}.
{counter,<0.39.0>}
29> flush().
Shell got {counter,6}
ok
30> LPid ! {increment, 2}.
{increment,2}
31> LPid ! {counter, self()}.
{counter,<0.39.0>}
32> flush().
Shell got {counter,8}
ok
33> LPid ! {code_switch, self()}.
{code_switch,<0.39.0>}
34> flush().
Shell got {switch,8}
Shell got {switched,8}
ok
35> LPid ! {counter, self()}.
{counter,<0.39.0>}
36> flush().
Shell got {counter,8}
ok
Can you maybe add some logs like io:format("DebugInfo:~p~n", [Sum]). to some of the functions to see what's going on?
When I call open_port without exit_status in the example below it is unusable:
Eshell V5.7.5 (abort with ^G)
1> P = open_port({spawn, "cat >bar"}, [stream, use_stdio]).
#Port<0.498>
2> port_command(P, "hello\n").
** exception error: bad argument
in function port_command/2
called as port_command(#Port<0.498>,"hello\n")
But when I just add exit_status and leave everything the same it works:
Eshell V5.7.5 (abort with ^G)
1> P = open_port({spawn, "cat >bar"}, [stream, use_stdio, exit_status]).
#Port<0.498>
2> port_command(P, "hello\n").
true
From the documentation I don't understand the difference in behaviour.
When you redirect output in a file in cat >bar command shell closes stdout and Erlang just closes the port in this case because ports try to consume command output by default and close on eof. The right way to fix it is to use out option as butter71 already suggested. Options like out, exit_status, error_to_stdout tell ports to not bother about stdout.
try setting the port for output only:
P = open_port({spawn, "cat >bar"}, [stream, use_stdio, out]).
port_command(P, "hello\n").
my guess is that erlang is unhappy because you redirected the command's stdout.