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 给出的正确代码是:
- %% ---
- %% Excerpted from "Programming Erlang"
- %% Copyrights apply to this code. It may not be used to create training material,
- %% courses, books, articles, and the like. Contact us if you are in doubt.
- %% We make no guarantees that this code is fit for any purpose.
- %% Visit http://www.pragmaticprogrammer.com/titles/jaerlang for more book information.
- %%---
- -module(chat_client).
- -import(io_widget,
- [get_state/1, insert_str/2, set_prompt/2, set_state/2,
- set_title/2, set_handler/2, update_state/3]).
- -export([start/0, test/0, connect/5]).
- start() ->
- connect("localhost", 2223, "AsDT67aQ", "general", "joe").
- test() ->
- connect("localhost", 2223, "AsDT67aQ", "general", "joe"),
- connect("localhost", 2223, "AsDT67aQ", "general", "jane"),
- connect("localhost", 2223, "AsDT67aQ", "general", "jim"),
- connect("localhost", 2223, "AsDT67aQ", "general", "sue").
- connect(Host, Port, HostPsw, Group, Nick) ->
- spawn(fun() -> handler(Host, Port, HostPsw, Group, Nick) end).
- handler(Host, Port, HostPsw, Group, Nick) ->
- process_flag(trap_exit, true),
- Widget = io_widget:start(self()),
- set_title(Widget, Nick),
- set_state(Widget, Nick),
- set_prompt(Widget, [Nick, " > "]),
- set_handler(Widget, fun parse_command/1),
- start_connector(Host, Port, HostPsw),
- disconnected(Widget, Group, Nick).
- disconnected(Widget, Group, Nick) ->
- receive
- {connected, MM} ->
- insert_str(Widget, "connected to server\nsending data\n"),
- lib_chan_mm:send(MM, {login, Group, Nick}),
- wait_login_response(Widget, MM);
- {Widget, destroyed} ->
- exit(died);
- {status, S} ->
- insert_str(Widget, to_str(S)),
- disconnected(Widget, Group, Nick);
- Other ->
- io:format("chat_client disconnected unexpected:~p~n",[Other]),
- disconnected(Widget, Group, Nick)
- end.
- wait_login_response(Widget, MM) ->
- receive
- {chan, MM, ack} ->
- active(Widget, MM);
- Other ->
- io:format("chat_client login unexpected:~p~p~n",[Other,MM]),
- wait_login_response(Widget, MM)
- end.
- active(Widget, MM) ->
- receive
- {Widget, Nick, Str} ->
- lib_chan_mm:send(MM, {relay, Nick, Str}),
- active(Widget, MM);
- {chan,MM,{msg,From,Pid,Str}} ->
- insert_str(Widget, [From,"@",pid_to_list(Pid)," ", Str, "\n"]),
- active(Widget, MM);
- {'EXIT',Widget,windowDestroyed} ->
- lib_chan_mm:close(MM);
- {close, MM} ->
- exit(serverDied);
- Other ->
- io:format("chat_client active unexpected:~p~n",[Other]),
- active(Widget, MM)
- end.
- start_connector(Host, Port, Pwd) ->
- S = self(),
- spawn_link(fun() -> try_to_connect(S, Host, Port, Pwd) end).
- try_to_connect(Parent, Host, Port, Pwd) ->
- %% Parent is the Pid of the process that spawned this process
- case lib_chan:connect(Host, Port, chat, Pwd, []) of
- {error, _Why} ->
- Parent ! {status, {cannot, connect, Host, Port}},
- sleep(2000),
- try_to_connect(Parent, Host, Port, Pwd);
- {ok, MM} ->
- lib_chan_mm:controller(MM, Parent),
- Parent ! {connected, MM},
- exit(connectorFinished)
- end.
- sleep(T) ->
- receive
- after T -> true
- end.
- to_str(Term) ->
- io_lib:format("~p~n",[Term]).
- parse_command(Str) -> skip_to_gt(Str).
- skip_to_gt(">" ++ T) -> T;
- skip_to_gt([_|T]) -> skip_to_gt(T);
- skip_to_gt([]) -> exit("no >").
- %% ---
- %% Excerpted from "Programming Erlang"
- %% Copyrights apply to this code. It may not be used to create training material,
- %% courses, books, articles, and the like. Contact us if you are in doubt.
- %% We make no guarantees that this code is fit for any purpose.
- %% Visit http://www.pragmaticprogrammer.com/titles/jaerlang for more book information.
- %%---
- -module(chat_group).
- -import(lib_chan_mm, [send/2, controller/2]).
- -import(lists, [foreach/2, reverse/2]).
- -export([start/2]).
- start(C, Nick) ->
- process_flag(trap_exit, true),
- controller(C, self()),
- send(C, ack),
- self() ! {C, {relay, Nick, "I'm starting the group"}},
- group_controller([{C,Nick}]).
- delete(Pid, [{Pid,Nick}|T], L) -> {Nick, reverse(T, L)};
- delete(Pid, [H|T], L) -> delete(Pid, T, [H|L]);
- delete(_, [], L) -> {"????", L}.
- group_controller([]) ->
- exit(allGone);
- group_controller(L) ->
- receive
- {chan, C, {relay, Nick, Str}} ->
- foreach(fun({Pid,_}) -> send(Pid, {msg, Nick, C, Str}) end, L),
- group_controller(L);
- {login, C, Nick} ->
- controller(C, self()),
- send(C, ack),
- self() ! {chan, C, {relay, Nick, "I'm joining the group"}},
- group_controller([{C,Nick}|L]);
- {chan_closed,C} ->
- {Nick, L1} = delete(C, L, []),
- self() ! {chan, C, {relay, Nick, "I'm leaving the group"}},
- group_controller(L1);
- Any ->
- io:format("group controller received Msg=~p~n", [Any]),
- group_controller(L)
- end.
已经确认,英文的定稿版本是有这个 bug 的。中文的译版,应该可以被 fix 掉了。


Write a Comment