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

David Christensen dpchrist@REDACTED
Fri Dec 31 18:55:42 CET 2021


> 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