Erlang-China

erlang 中文社区

Programming Erlang 实例之中的 bug


Arbow 的 blog 提到他发现了 Programming Erlang 实例代码中的一个 bug。

拿到了Programming Erlang的电子书,研究一下Potian先前跟俺说的“Socket Base Distribution”部分代码,结果发现chat_client.erl(对应书上的Chapter 11 IRC Lite),运行出错……。

而在 Maillist 里 Joe Armstrong 本人也确认了这个bug。

There *was* a bug after all - there was a patch by Kazuya Sakakihara
on the errata list for the book.

I missed this since I when I retested the version I distributed it seemed to work (very strange). I’ve now fixed the code and the changes should make it into the next printing of the book.

The corrected versions of code/socket_dist/chat_client.erl and code/socket_dist/chat_group.erl are attached to this mail.

Joe Armstrong 给出的正确代码是:

  1. %% ---
  2. %%  Excerpted from "Programming Erlang"
  3. %%  Copyrights apply to this code. It may not be used to create training material,
  4. %%  courses, books, articles, and the like. Contact us if you are in doubt.
  5. %%  We make no guarantees that this code is fit for any purpose.
  6. %%  Visit http://www.pragmaticprogrammer.com/titles/jaerlang for more book information.
  7. %%---
  8. -module(chat_client).
  9.  
  10. -import(io_widget,
  11.     [get_state/1, insert_str/2, set_prompt/2, set_state/2,
  12.      set_title/2, set_handler/2, update_state/3]).
  13.  
  14. -export([start/0, test/0, connect/5]).
  15.  
  16.  
  17. start() ->
  18.     connect("localhost", 2223, "AsDT67aQ", "general", "joe").
  19.  
  20.  
  21. test() ->
  22.     connect("localhost", 2223, "AsDT67aQ", "general", "joe"),
  23.     connect("localhost", 2223, "AsDT67aQ", "general", "jane"),
  24.     connect("localhost", 2223, "AsDT67aQ", "general", "jim"),
  25.     connect("localhost", 2223, "AsDT67aQ", "general", "sue").
  26.       
  27.  
  28. connect(Host, Port, HostPsw, Group, Nick) ->
  29.     spawn(fun() -> handler(Host, Port, HostPsw, Group, Nick) end).
  30.                 
  31. handler(Host, Port, HostPsw, Group, Nick) ->
  32.     process_flag(trap_exit, true),
  33.     Widget = io_widget:start(self()),
  34.     set_title(Widget, Nick),
  35.     set_state(Widget, Nick),
  36.     set_prompt(Widget, [Nick, " > "]),
  37.     set_handler(Widget, fun parse_command/1),
  38.     start_connector(Host, Port, HostPsw),   
  39.     disconnected(Widget, Group, Nick).
  40.  
  41.  
  42.  
  43. disconnected(Widget, Group, Nick) ->
  44.     receive
  45.     {connected, MM} ->
  46.         insert_str(Widget, "connected to server\nsending data\n"),
  47.         lib_chan_mm:send(MM, {login, Group, Nick}),
  48.         wait_login_response(Widget, MM);
  49.     {Widget, destroyed} ->
  50.         exit(died);
  51.     {status, S} ->
  52.         insert_str(Widget, to_str(S)),
  53.         disconnected(Widget, Group, Nick);
  54.     Other ->
  55.         io:format("chat_client disconnected unexpected:~p~n",[Other]),
  56.         disconnected(Widget, Group, Nick)
  57.     end.
  58.  
  59.  
  60.  
  61. wait_login_response(Widget, MM) ->
  62.     receive
  63.     {chan, MM, ack} ->
  64.         active(Widget, MM);
  65.     Other ->
  66.         io:format("chat_client login unexpected:~p~p~n",[Other,MM]),
  67.         wait_login_response(Widget, MM)
  68.     end.
  69.  
  70.  
  71.  
  72. active(Widget, MM) ->
  73.      receive
  74.      {Widget, Nick, Str} ->
  75.          lib_chan_mm:send(MM, {relay, Nick, Str}),
  76.          active(Widget, MM);
  77.      {chan,MM,{msg,From,Pid,Str}} ->
  78.          insert_str(Widget, [From,"@",pid_to_list(Pid)," ", Str, "\n"]),
  79.          active(Widget, MM);
  80.      {'EXIT',Widget,windowDestroyed} ->
  81.          lib_chan_mm:close(MM);
  82.      {close, MM} ->
  83.          exit(serverDied);
  84.      Other ->
  85.          io:format("chat_client active unexpected:~p~n",[Other]),
  86.          active(Widget, MM)
  87.      end.
  88.  
  89.  
  90.  
  91. start_connector(Host, Port, Pwd) ->
  92.     S = self(),
  93.     spawn_link(fun() -> try_to_connect(S, Host, Port, Pwd) end).
  94.    
  95. try_to_connect(Parent, Host, Port, Pwd) ->
  96.     %% Parent is the Pid of the process that spawned this process
  97.     case lib_chan:connect(Host, Port, chat, Pwd, []) of
  98.     {error, _Why} ->
  99.         Parent ! {status, {cannot, connect, Host, Port}},
  100.         sleep(2000),
  101.         try_to_connect(Parent, Host, Port, Pwd);
  102.     {ok, MM} ->
  103.         lib_chan_mm:controller(MM, Parent),
  104.         Parent ! {connected, MM},
  105.         exit(connectorFinished)
  106.     end.
  107.  
  108.  
  109. sleep(T) ->
  110.     receive
  111.     after T -> true
  112.     end.
  113.        
  114. to_str(Term) ->
  115.     io_lib:format("~p~n",[Term]).
  116.  
  117. parse_command(Str) -> skip_to_gt(Str).
  118.  
  119. skip_to_gt(">" ++ T) -> T;
  120. skip_to_gt([_|T])    -> skip_to_gt(T);
  121. skip_to_gt([])       -> exit("no >").
  1. %% ---
  2. %%  Excerpted from "Programming Erlang"
  3. %%  Copyrights apply to this code. It may not be used to create training material,
  4. %%  courses, books, articles, and the like. Contact us if you are in doubt.
  5. %%  We make no guarantees that this code is fit for any purpose.
  6. %%  Visit http://www.pragmaticprogrammer.com/titles/jaerlang for more book information.
  7. %%---
  8.  
  9. -module(chat_group).
  10. -import(lib_chan_mm, [send/2, controller/2]).
  11. -import(lists, [foreach/2, reverse/2]).
  12.  
  13. -export([start/2]).
  14.  
  15. start(C, Nick) ->
  16.     process_flag(trap_exit, true),
  17.     controller(C, self()),
  18.     send(C, ack),
  19.     self() ! {C, {relay, Nick, "I'm starting the group"}},
  20.     group_controller([{C,Nick}]).
  21.  
  22.  
  23.  
  24. delete(Pid, [{Pid,Nick}|T], L) -> {Nick, reverse(T, L)};
  25. delete(Pid, [H|T], L)          -> delete(Pid, T, [H|L]);
  26. delete(_, [], L)               -> {"????", L}.
  27.  
  28.  
  29.  
  30. group_controller([]) ->
  31.     exit(allGone);
  32. group_controller(L) ->
  33.     receive
  34.     {chan, C, {relay, Nick, Str}} ->
  35.         foreach(fun({Pid,_}) -> send(Pid, {msg, Nick, C, Str}) end, L),
  36.         group_controller(L);
  37.     {login, C, Nick} ->
  38.         controller(C, self()),
  39.         send(C, ack),
  40.         self() ! {chan, C, {relay, Nick, "I'm joining the group"}},
  41.         group_controller([{C,Nick}|L]);
  42.     {chan_closed,C} ->
  43.         {Nick, L1} = delete(C, L, []),
  44.         self() ! {chan, C, {relay, Nick, "I'm leaving the group"}},
  45.         group_controller(L1);
  46.     Any ->
  47.         io:format("group controller received Msg=~p~n", [Any]),
  48.         group_controller(L)
  49.     end.

已经确认,英文的定稿版本是有这个 bug 的。中文的译版,应该可以被 fix 掉了。







Write a Comment

Note: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>