Programming Erlang, 2 e., Chapter 2, Exercise 4, put_file()

Fred Youhanaie fly@REDACTED
Sat Jan 1 13:45:19 CET 2022


Hi David

I'll just add a few pointers to what has already been said.

Since you're reusing previous code, you may find the ?MODULE macro handy, e.g. spawn(?MODULE, loop, [Dir]), which should save you from repeating the module name inside the code.

Within the shell, you can use "flush()." to receive and print all the messages in the shell's message queue, if any. Using the "receive X -> X end." pattern will block the shell if the message queue 
is empty, as you have already experienced.

For unknown messages, it is probably not a good idea to attempt to reply to unexpected, perhaps even malformed, messages. The simplest pattern to use in the server, while you're learning, would be 
something like:

receive
   ...
   Any_msg -> %% make this the last clause in the receive block
     io:format("Unknown message: ~p~n", [Any_msg])
end

I think the above do appear at various places in Joe's book.

Enjoy the rest of the book :-)

Cheers
Fred




On 31/12/2021 17:55, David Christensen wrote:
>> On Fri, Dec 31, 2021, 08:13 David Christensen <dpchrist@REDACTED>
>> wrote:
>>
>>> erlang-questions:
>>>
>>> I am working my way through "Programming Erlang", 2 e.:
>>>
>>> 2021-12-30 20:00:05 dpchrist@REDACTED ~/sandbox/erlang
>>> $ cat /etc/debian_version ; uname -a ; dpkg-query -W erlang
>>> 9.13
>>> Linux tinkywinky 4.9.0-17-amd64 #1 SMP Debian 4.9.290-1 (2021-12-12)
>>> x86_64 GNU/Linux
>>> erlang  1:19.2.1+dfsg-2+deb9u3
>>>
>>>
>>> Chapter 2, Exercise 4, requests that I add a "put_file" command to the
>>> file client and server code.
> 
>>> I have extended afile_server.erl with a "put_file" command that calls
>>> file::write_file():
> 
> 
> On 12/31/21 5:31 AM, Leonard B wrote:
>  > Compare the receive pattern for "{Client, {put_file, File, Bytes}}" to
>  > what you're sending
> 
> 
> Ignoring the cut-and-paste error that you identified below, this server code is sending a message to the client:
> 
> Client ! {self(), file:write_file(Full, Bytes)}
> 
> 
> E.g. a tuple consisting of the client PID and the return value of write_file().
> 
> 
> RTFM file(3erl) says:
> 
> write_file(Filename, Bytes) -> ok | {error, Reason}
> 
> 
> So, the message sent should be either the tuple:
> 
> {self(), ok}
> 
> 
> Or the tuple:
> 
> {self(), {error, Reason}}
> 
> 
> The client (shell) is attempting to receive via this code:
> 
> receive C -> C end.
> 
> 
> AIUI the receive pattern is a single variable, C, which should match a single value or a tuple.
> 
> 
> So, after receiving, the client value of C should be either:
> 
> {<0.64.0>, ok}
> 
> 
> Or:
> 
> {<0.64.0>, {error, Reason}}
> 
> 
> Therefore, I believe the receive pattern (a variable) matches what is being sent (a tuple).
> 
> 
> On 12/31/21 5:47 AM, Leonard B wrote:
>  > Sorry, replied from mobile and had issues reading the code.
>  >
>  > What I do see is you're spawning a process using a*different module*
>  > (probably a previous file you were working on).
>  >
>  > start(Dir) -> spawn(afile_server, loop, [Dir]).
>  >
>  > ^^ that should probably be
>  >
>  > start(Dir) -> spawn(ex0204_server, loop, [Dir]).
>  >
>  > Kind regards,
>  > Leonard
> 
> 
> Thank you for identifying the cut-and-paste error in ex0204_server.erl.  The first argument to spawn() is wrong:
> 
> start(Dir) -> spawn(afile_server, loop, [Dir]).
> 
> 
> The code should be:
> 
> start(Dir) -> spawn(ex0204_server, loop, [Dir]).
> 
> 
> Here is the corrected code and a sample run:
> 
> 2021-12-31 08:38:52 dpchrist@REDACTED ~/sandbox/erlang
> $ cat ex0204_server.erl
> -module(ex0204_server).
> -export([start/1, loop/1]).
> 
> start(Dir) -> spawn(ex0204_server, loop, [Dir]).
> 
> loop(Dir) ->
>      receive
>      {Client, list_dir} ->
>          Client ! {self(), file:list_dir(Dir)};
>      {Client, {get_file, File}} ->
>          Full = filename:join(Dir, File),
>          Client ! {self(), file:read_file(Full)};
>      {Client, {put_file, File, Bytes}} ->
>          Full = filename:join(Dir, File),
>          Client ! {self(), file:write_file(Full, Bytes)}
>      end,
>      loop(Dir).
> 
> 2021-12-31 08:39:45 dpchrist@REDACTED ~/sandbox/erlang
> $ erl
> Erlang/OTP 19 [erts-8.2.1] [source] [64-bit] [smp:8:8] [async-threads:10] [kernel-poll:false]
> 
> Eshell V8.2.1  (abort with ^G)
> 1> c(ex0204_server).
> {ok,ex0204_server}
> 2> Server = ex0204_server:start(".").
> <0.64.0>
> 3> Server ! {self(), list_dir}.
> {<0.57.0>,list_dir}
> 4> receive A -> A end.
> {<0.64.0>,
>   {ok,["Makefile",".ex0204_server.erl.swp","afile_client.erl",
>        "ex0204_server.erl","ex0204_client.erl","hello.erl",
>        "afile_server.erl","CVS","afile_server.run",
>        "ex0204_server.beam"]}}
> 5> Server ! {self(), {get_file, "hello.erl"}}.
> {<0.57.0>,{get_file,"hello.erl"}}
> 6> receive B -> B end.
> {<0.64.0>,
>   {ok,<<"-module(hello).\n-export([start/0]).\n\nstart() ->\n io:format(\"hello, world!~n\").\n">>}}
> 7> Server ! {self(), {put_file, "foo.txt", "foo on you!\n"}}.
> {<0.57.0>,{put_file,"foo.txt","foo on you!\n"}}
> 8> receive C -> C end.
> {<0.64.0>,ok}
> 9> halt().
> 
> 2021-12-31 08:40:44 dpchrist@REDACTED ~/sandbox/erlang
> $ cat foo.txt
> foo on you!
> 
> 
> Now that we have figured out the bug -- the shell sent a command to the server for which there was no matching receive pattern -- it begs the question: how do I detect when a process is sent a message 
> for which there is no matching receive pattern?
> 
> 
> Here is a naive hack:
> 
> 2021-12-31 09:31:36 dpchrist@REDACTED ~/sandbox/erlang
> $ cat ex0204_server.erl
> -module(ex0204_server).
> -export([start/1, loop/1]).
> 
> start(Dir) -> spawn(ex0204_server, loop, [Dir]).
> 
> loop(Dir) ->
>      receive
>      {Client, list_dir} ->
>          Client ! {self(), file:list_dir(Dir)};
>      {Client, {get_file, File}} ->
>          Full = filename:join(Dir, File),
>          Client ! {self(), file:read_file(Full)};
>      {Client, {put_file, File, Bytes}} ->
>          Full = filename:join(Dir, File),
>          Client ! {self(), file:write_file(Full, Bytes)};
>      {Client, U} ->
>          Client ! {self(), {error, "Unknown command", U}};
>      {Client, U, A} ->
>          Client ! {self(), {error, "Unknown command", U, A}};
>      {Client, U, A, B} ->
>          Client ! {self(), {error, "Unknown command", U, A, B}}
>      end,
>      loop(Dir).
> 
> 2021-12-31 09:31:38 dpchrist@REDACTED ~/sandbox/erlang
> $ erl
> Erlang/OTP 19 [erts-8.2.1] [source] [64-bit] [smp:8:8] [async-threads:10] [kernel-poll:false]
> 
> Eshell V8.2.1  (abort with ^G)
> 1> c(ex0204_server).
> {ok,ex0204_server}
> 2> Server = ex0204_server:start(".").
> <0.64.0>
> 3> Server ! {self(), foo}
> 3> .
> {<0.57.0>,foo}
> 4> receive A -> A end.
> {<0.64.0>,{error,"Unknown command",foo}}
> 5> Server ! {self(), foo, bar}.
> {<0.57.0>,foo,bar}
> 6> receive B -> B end.
> {<0.64.0>,{error,"Unknown command",foo,bar}}
> 7> Server ! {self(), foo, bar, baz}.
> {<0.57.0>,foo,bar,baz}
> 8> receive C -> C end.
> {<0.64.0>,{error,"Unknown command",foo,bar,baz}}
> 9> halt().
> 
> 
> (I presume Chapters 3+ will provide better answers.)
> 
> 
> Thank you,
> 
> David
> 
> 
> p.s.  I do not see usage guidelines for erlang-questions on:
> 
> https://erlang.org/mailman/listinfo/erlang-questions
> 
> Please let me know if there are usage guidelines, FAQ, etc., for erlang-questions.
> 
> 
> p.p.s. When using a mailing list:
> 
> 1.  I "Reply to List", and do not CC the previous author (I presume they are subscribed).
> 
> 2.  I use (trimmed) bottom/ inline posting style.
> 
> Please let me know if erlang-questions uses other conventions.


More information about the erlang-questions mailing list