Re: Programming Erlang, 2 e., Chapter 2, Exercise 4, put_file()
David Christensen <[email protected]> Fri, 31 Dec 2021 09:55:42 -0800
| Newsgroups | gmane.comp.lang.erlang.general |
|---|---|
| Message-ID | <[email protected]> |
> On Fri, Dec 31, 2021, 08:13 David Christensen <[email protected]> > wrote: > >> erlang-questions: >> >> I am working my way through "Programming Erlang", 2 e.: >> >> 2021-12-30 20:00:05 dpchrist@tinkywinky ~/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@tinkywinky ~/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@tinkywinky ~/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@tinkywinky ~/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@tinkywinky ~/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@tinkywinky ~/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.