2006/12/25

Erlang OTP Design

OTP としましては。。。
Application(try_app.erl), Supervisor(try_sup.erl), Worker(try_srv.erl) とある。
Application を start すると Supervisor プロセスが起動する。
Supervisor は Worker プロセスを管理する。
Worker プロセスが仕事を行う。

3> lc([try_app, try_sup, try_srv]).
ok
4> application:start(try_app).
ok
5> gen_server:call(try_srv, 1).
{"Hello World!",1}
6> gen_server:call(try_srv, banobion).
{"Hello World!",banobion}


try_app.erl
%%%-------------------------------------------------------------------
%%% File : try_app.erl
%%% Author :
%%% Description : OTPのお試し
%%%
%%% Created : 25 Dec 2006 by
%%%-------------------------------------------------------------------
-module(try_app).

-behaviour(application).

%% Application callbacks
-export([start/2, stop/1]).

%%====================================================================
%% Application callbacks
%%====================================================================
%%--------------------------------------------------------------------
%% Function: start(Type, StartArgs) -> {ok, Pid} |
%% {ok, Pid, State} |
%% {error, Reason}
%% Description: This function is called whenever an application
%% is started using application:start/1,2, and should start the processes
%% of the application. If the application is structured according to the
%% OTP design principles as a supervision tree, this means starting the
%% top supervisor of the tree.
%%--------------------------------------------------------------------
start(_Type, StartArgs) ->
case try_sup:start_link(StartArgs) of
{ok, Pid} ->
{ok, Pid};
Error ->
Error
end.

%%--------------------------------------------------------------------
%% Function: stop(State) -> void()
%% Description: This function is called whenever an application
%% has stopped. It is intended to be the opposite of Module:start/2 and
%% should do any necessary cleaning up. The return value is ignored.
%%--------------------------------------------------------------------
stop(_State) ->
ok.

%%====================================================================
%% Internal functions
%%====================================================================


try_app.app
%% -*-erlang-*-
{application, try_app,
[{description, "OTP のお試し"},
{vsn, "0.1"},
{modules, [try_app, try_sup, try_srv]},
{registered, [try_app]},
{applications, [kernel, stdlib]},
{mod, {try_app,[]}}
]}.


try_sup.erl
%%%-------------------------------------------------------------------
%%% File : try_sup.erl
%%% Author :
%%% Description : OTP のお試し
%%%
%%% Created : 25 Dec 2006 by
%%%-------------------------------------------------------------------
-module(try_sup).

-behaviour(supervisor).

%% API
-export([start_link/1]).

%% Supervisor callbacks
-export([init/1]).

-define(SERVER, ?MODULE).

%%====================================================================
%% API functions
%%====================================================================
%%--------------------------------------------------------------------
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
%% Description: Starts the supervisor
%%--------------------------------------------------------------------
start_link(_StartArgs) ->
supervisor:start_link({local, ?SERVER}, ?MODULE, []).

%%====================================================================
%% Supervisor callbacks
%%====================================================================
%%--------------------------------------------------------------------
%% Func: init(Args) -> {ok, {SupFlags, [ChildSpec]}} |
%% ignore |
%% {error, Reason}
%% Description: Whenever a supervisor is started using
%% supervisor:start_link/[2,3], this function is called by the new process
%% to find out about restart strategy, maximum restart frequency and child
%% specifications.
%%--------------------------------------------------------------------
init([]) ->
AChild = {try_srv,{try_srv,start_link,[]},
permanent,2000,worker,[try_srv]},
{ok,{{one_for_all,0,1}, [AChild]}}.

%%====================================================================
%% Internal functions
%%====================================================================


try_srv.erl
%%%-------------------------------------------------------------------
%%% File : try_srv.erl
%%% Author :
%%% Description : OTP のお試し
%%%
%%% Created : 25 Dec 2006 by
%%%-------------------------------------------------------------------
-module(try_srv).

-behaviour(gen_server).

%% API
-export([start_link/0]).

%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).

-define(SERVER, try_srv).

-record(state, {message = "Hello World!"}).

%%====================================================================
%% API
%%====================================================================
%%--------------------------------------------------------------------
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
%% Description: Starts the server
%%--------------------------------------------------------------------
start_link() ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).

%%====================================================================
%% gen_server callbacks
%%====================================================================

%%--------------------------------------------------------------------
%% Function: init(Args) -> {ok, State} |
%% {ok, State, Timeout} |
%% ignore |
%% {stop, Reason}
%% Description: Initiates the server
%%--------------------------------------------------------------------
init([]) ->
{ok, #state{}}.

%%--------------------------------------------------------------------
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
%% {reply, Reply, State, Timeout} |
%% {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, Reply, State} |
%% {stop, Reason, State}
%% Description: Handling call messages
%%--------------------------------------------------------------------
handle_call(Request, _From, State) ->
Reply = {State#state.message, Request},
{reply, Reply, State}.

%%--------------------------------------------------------------------
%% Function: handle_cast(Msg, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% Description: Handling cast messages
%%--------------------------------------------------------------------
handle_cast(_Msg, State) ->
{noreply, State}.

%%--------------------------------------------------------------------
%% Function: handle_info(Info, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% Description: Handling all non call/cast messages
%%--------------------------------------------------------------------
handle_info(_Info, State) ->
{noreply, State}.

%%--------------------------------------------------------------------
%% Function: terminate(Reason, State) -> void()
%% Description: This function is called by a gen_server when it is about to
%% terminate. It should be the opposite of Module:init/1 and do any necessary
%% cleaning up. When it returns, the gen_server terminates with Reason.
%% The return value is ignored.
%%--------------------------------------------------------------------
terminate(_Reason, _State) ->
ok.

%%--------------------------------------------------------------------
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
%% Description: Convert process state when code is changed
%%--------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
{ok, State}.

%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------

2006/12/22

Erlang fib

-module(fib).
-export([fib/1, fib2/1, start/0, server/1]).

fib(1) -> 1;

fib(2) -> 1;

fib(N) ->
fib(N-1) + fib(N-2).

fib2(N) ->
fib_server ! {N, self()},
receive
{ok, Value} ->
Value;
_ ->
Result = fib2(N - 1) + fib2(N - 2),
fib_server ! {add, N, Result},
Result
end.

server(Dict) ->
receive
{N, Client} ->
case dict:find(N, Dict) of
{ok, Value} ->
Client ! {ok, Value},
fib:server(Dict);
_ -> Client ! nil,
fib:server(Dict)
end;
{add, N, Result} ->
fib:server(dict:store(N, Result, Dict));
dump -> io:format("~p~n", [Dict]),
fib:server(Dict)
end.

start() ->
Dict = dict:new(),
Dict1 = dict:store(1, 1, Dict),
Dict2 = dict:store(2, 1, Dict1),
Pid = spawn(fib, server, [Dict2]),
register(fib_server, Pid).

2006/12/19

Erlang ODBC

Oracle との ODBC 接続がうまくいかなくて悩んでいたけど、ドライバを Microsoft ODBC for Oracle にしたらすんなりつながった。
その後、Oracreのドライバでも {scrollable_cursors, off} を指定してやればつながることが判明。
connect する都度 odbcserver.exe が起動され disconnect しないとそのプロセスが残る。

-module(odbc_test).

-export([main/0]).

main() ->
{ok, Ora} = odbc:connect("DSN=bino;UID=hr;PWD=password;", [{scrollable_cursors, off}]),
{selected, _, [{Aiu}]} = odbc:sql_query(Ora, "select 'あいう' from dual"),
io:format("~s~n", [Aiu]),
odbc:disconnect(Ora).

2006/12/18

Erlang コンカレントプログラミング

Erlang の特徴1つとして Concurrent Programming が挙げられる。OS(カーネル)レベルのスレッドではなく、ユーザレベルのスレッドであるが、非常に軽量で他の言語のスレッドプログラミングでは実現できないような、多重実行も可能らしい。
process(thread)間で共有するデータがない、という理由から、Erlang では thread ではなく process という言葉を使っている。

-module(concurrent_try).

-export([foo/2, run_foo/0]).

foo(_, 0) ->
io:format("おしまい。~n");
foo(Str, RepeatTime) ->
io:format(Str),
foo(Str, RepeatTime - 1).

run_foo() ->
spawn(concurrent_try, foo, ["ひつじ~n", 3]),
spawn(concurrent_try, foo, ["かもめ~n", 3]).

これを実行すると次のようになる。
18> concurrent_try:run_foo().
ひつじ
かもめ
<0.98.0>ひつじ
かもめ

ひつじ
かもめ
19> おしまい。
19> おしまい。


ビルトインファンクションの spawn(Module, Exported_Function, List of Arguments) で新しいプロセス作成する。

出力に混ざってる <0.98.0> は2つめの spawn の返り値で "process identifier" または "pid" である。プロセス固有の識別子である。

メッセージ送受信


プロセス間の共有データのかわりに、プロセス間ではメッセージの送受信が行われる。
receive でメッセージを受信し、! でメッセージを送信する。
server_loop() ->
receive Str ->
io:format("「~s」~n", [Str])
end,
server_loop().

45> ServerPID = spawn(concurrent_try, server_loop, []).
<0.161.0>
46> ServerPID ! "あいう".
「あいう」
[130,160,130,162,130,164]

Erlang

関数を作る


対話環境の中では関数を作ることはできない。ファイルに書いておいて、それをコンパイルすることによって行なう。次のようなファイル(koto.erl)を作成する。C-c C-k でコンパイルされる。
-module(koto).

-export([foo/1]).

foo(X) ->
X + 1.

1> c("c:/home/ancient/letter/erlang/a/koto", [{outdir, "c:/home/ancient/letter/erlang/a/"}]).
{ok,koto}
2> koto:foo(1).
2


-module はモジュールの定義(?)でファイル名と一致する必要があるみたい。
-export はそのモジュールが公開する関数を 関数名/引数の数 のリストで指定する。

Erlang

日本語


Meadow 上なら io:format で日本語を出力できるみたい。
7> io:format("あいう\n").
あいう
ok

2006/12/17

mozex(Firefox & Emacs)


mozex の開発バージョンをダウンロードし、インストール。
設定は画像参照。
~/.emacs で文字コードを指定しておく。

(add-to-list
'file-coding-system-alist
'("/home/ancient/tmp/mozex/.*" . (utf-8-unix . utf-8-unix)))

Erlang

Erlang を試してみようかと思う。

インストール


http://www.erlang.org/download.html からダウンロードした exe を実行すれば、インストール完了。

対話環境


スタートメニューから Erlang を起動すれば、対話環境が起ち上がる。
Ctrl-p, Ctrl-n で入力履歴。Ctrl-a, e, e, f, b は emace と同じ。
末尾はピリオド。
1> 1.
1
2> 1 + 3.
4


Emacs(Meadow)からの実行


Erlang をインストールしたディレクトリが C:\Program Files\erl5.5.2 の場合、.emacs に次のように記述する。
(setq load-path (cons  "C:/Program Files/erl5.5.2/lib/tools-2.5.2/emacs"
load-path))
(setq erlang-root-dir "C:/Program Files/erl5.5.2")
(setq exec-path (cons "C:/Program Files/erl5.5.2/bin" exec-path))
(require 'erlang-start)

M-x erlang-shell で対話環境が起動する。

日本語


Erlang では文字列は数値の配列のようで次のようになる。
2> "あいう".
[130,160,130,162,130,164]

入出力で日本語をまともに扱うには何らかのライブラリが必要なようだ。

友人来訪

飯塚夫妻とニッシーに会った。結婚式以来だろうか。
それぞれの環境や境遇は変わっても、みんなぜんぜん変わってなかったな。

2006/12/16

Barbour CLASSIC MOORLAND

13年ぶりに冬のアウターを買いました。
Barbour のクラシックムーアランド(サイズ36、色オリーブ)です。
この13年間来ていたのは Whole Earth の青いマウンテンパーカー。長い間ありがとうございました。
ゴアテックスからオイルドコットンへの変遷は何を暗示しているのでしょうか。

2006/12/13

exe

うごかない?

(require :trivial-sockets)

(defun main1 ()
(trivial-sockets:with-server (server (:port 8989))
(let ((socket (sb-bsd-sockets:socket-accept server)))
(print (sb-bsd-sockets:socket-peername socket))
(sb-bsd-sockets:socket-close socket))))

(sb-ext:save-lisp-and-die "test.exe" :purify t :toplevel #'main1 :executable t)


これは動く
sbcl --eval "(save-lisp-and-die \"bano.exe\" :toplevel (lambda () (princ \"bano\") (terpri) 0) :executable t)" 

接続してきたアドレスを表示する

(trivial-sockets:with-server (server (:port 8989))
(let ((socket (sb-bsd-sockets:socket-accept server)))
(print (sb-bsd-sockets:socket-peername socket))
(sb-bsd-sockets:socket-close socket)))

2006/12/10

タコのパエリア

タコのパエリアを作りました。大好評でした。

2006/12/01

SBCL 1.0 リリース

SBCL 1.0 がリリースされました。
Shift-JIS external format も実装されています。すばらしい。