manywaypark's Blog
개발, 검색, 함수

erlang web frameworks

함수형 언어/Erlang 2013. 4. 10. 14:54 by manywaypark

erlang의 주요한 web framework들을 소개하는 좋은 자료가 있어 링크를 남김.

(저자는 yaws의 commiter임)

http://cufp.org/conference/sessions/2012/t2-erlang-web-frameworks-steve-vinoski-basho-techn


happy hackin'


Erlang의 풍부한 표현 방법을 보여주기 위한 간단한 예제를 만들었다.
lists:map에 전달하는 첫번째 파라미터(함수) 부분의 표현 방법을 보면 된다. 

by_...
function은  리스트를 입력받아 각 요소가 홀수인지 짝수인지 판별한다 (에러 처리 포함).
@spec (by_...([integer() | any()]) -> [odd | even | error].

기본적으로 유념해야 할 것은,
  • if는 guard expression을 사용하고, case는 pattern matching을 사용한다.
  • if는 마지막에 truecase는 마지막에 underscore(_) 또는 Any를 사용하여 default(catch all) 조건을 만든다.
  • guard의 and는 comma(,)이고, or는 semicolon(;)이다.

자세한 것은 코드 참조.

%%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: nil -*-
%%%-------------------------------------------------------------------
%%% File    : expressions.erl
%%% Author  : M.W. Park <manywaypark@gmail.com>
%%% Description : an erlang example (to explain expressions)
%%%               ref: http://www.erlang.org/doc/reference_manual/expressions.html
%%%
%%% Created : 2011-09-22 목요일 by M.W. Park
%%%-------------------------------------------------------------------

-module(expressions).
%% -export([]).
%% -import(, []).
-compile(export_all).

%% using guard inside 'if'
by_if(L) when is_list(L) ->
    lists:map(fun(X) ->
                      if
                          is_integer(X),
                          X rem 2 =:= 0 -> even;
                          is_integer(X) -> odd;
                          true          -> error
                      end
              end,
              L).

%% nested 'case'
by_case(L) when is_list(L) ->
    lists:map(fun(X) ->
                      case is_integer(X) of
                          true ->
                              case X rem 2 of
                                  0 -> even;
                                  _ -> odd
                              end;
                          _ -> error
                      end
              end,
              L).

%% 'case' w/ guards
by_case_with_guard(L) when is_list(L) ->
    lists:map(fun(X) ->
                      case X of
                          _ when is_integer(X),
                                 X rem 2 =:= 0 -> even;
                          _ when is_integer(X) -> odd;
                          _                    -> error
                      end
              end,
              L).

%% 'case' w/ exception
%% 'rem' operator may throw an exception.
by_case_with_ex(L) when is_list(L) ->
    lists:map(fun(X) ->
                      try 
                          case X rem 2 of
                              0 -> even;
                              _ -> odd
                          end
                      catch
                          _:_ -> error
                      end
              end,
              L).

%% pattern matching functions
'what?'(X) when is_integer(X), X rem 2 =:= 0 ->
    even;
'what?'(X) when is_integer(X) ->
    odd;
'what?'(_) ->
    error.

%% passing function name as a parameter
by_passing_fun(L) when is_list(L) ->
    lists:map(fun 'what?'/1, L).

%% anonymous function w/ guards
by_anonymous_fun(L) when is_list(L) ->
    lists:map(fun(X) when is_integer(X),
                          X rem 2 =:= 0 -> even;
                 (X) when is_integer(X) -> odd;
                 (_)                    -> error
              end,
              L).

%% mixture of anonymous function w/ a guard and 'case'
by_mix(L) when is_list(L) ->
    lists:map(fun(X) when is_integer(X) ->
                      case X rem 2 of
                          0 -> even;
                          _ -> odd
                      end;
                 (_) -> error
              end,
              L).

%%%-------------------------------------------------------------------
%%% Unit Tests
%%%-------------------------------------------------------------------
-include_lib("eunit/include/eunit.hrl").

-define(BASIC(FunName), ?_assertEqual([odd, even], FunName(lists:seq(1, 2)))).
-define(ERROR(FunName), ?_assertEqual([error, error], FunName([0.1, atom]))). 

by_if_test_() ->
    [
     ?BASIC(by_if),
     ?ERROR(by_if)
    ].

by_case_test_()->
    [
     ?BASIC(by_case),
     ?ERROR(by_case)
    ].

by_case_with_guard_test_() ->
    [
     ?BASIC(by_case_with_guard),
     ?ERROR(by_case_with_guard)
    ].

by_case_with_ex_test_() ->
    [
     ?BASIC(by_case_with_ex),
     ?ERROR(by_case_with_ex)
    ].

by_passing_fun_test_() ->
    [
     ?BASIC(by_passing_fun),
     ?ERROR(by_passing_fun)
    ].

by_anonymous_fun_test_() ->
    [
     ?BASIC(by_anonymous_fun),
     ?ERROR(by_anonymous_fun)
    ].

by_mix_test_() ->
    [
     ?BASIC(by_mix),
     ?ERROR(by_mix)
    ].

happy hackin'
Erlang Opcode
참고 문서에는 otp_src_R14B01/lib/compiler/src/beam_opcodes.erl에 있다고 설명하고있으나,
최신 소스에는 otp/dialyzer/test/options1_SUITE_data/src/compiler/beam_opcodes.erl에 있고 주석에 보면 beam_makeops를 이용해서 만들어졌다고 되어있다.
beam_makeopsotp/erts/emulator/utils에 있고 perl script이다.

erlang source(.erl)을 assembly code(.S)로 컴파일 할 수 있는데 아래처럼 compile flag에 "+'S'"를 추가 하면 된다.
파일 내용을 확인해보면 opcode들이 난무(?)한다. 
$ erlc "+'S'" foo.erl

BEAM file format
TBD - 아래 링크 참고

happy hackin'
 
refs:
결론부터 말하자면 복합키는 지원안된다 (erlang mnesia composite key 쯤으로 구글링하면 많이 나온다).
키에 tuple을 써서 비슷하게 만들 수 있다.
아래와 같이 레코드를 정의하고 쓰면 된다.
-record(id, {name=[], phone=[]}).
-record(person,  {pkey=#id{}, addr=[], note=[]}).

index 추가하면 되는 줄로 알고... 몇년전에 삽질했던 것을 오늘 또 했다. Orz...
다시 몇년 후를 기약(?)하며 오늘은 기록으로 남겨둔다.

happy hackin'

erlang build 하기 #2 - w/ wx

함수형 언어/Erlang 2009. 9. 16. 14:23 by manywaypark
erlang build하기 첫번째 글을 쓸 때는 erlang에 wx 같은 라이브러리가 포함되어 있지 않았다 (R13 이상부터 포함된것같다).
첫번째 글에 설명한 일반적인 설정을 기준으로 configure를 실행하면, 중간에 다음과 같은 경고가 표시된다.
configure: WARNING:
                wxWidgets must be installed on your system.

                Please check that wx-config is in path, the directory
                where wxWidgets libraries are installed (returned by
                'wx-config --libs' or 'wx-config --static --libs' command)
                is in LD_LIBRARY_PATH or equivalent variable and
                wxWidgets version is 2.8.0 or above.


configure 마지막에는 다음과 같은 메시지를 출력한다(빌드시 wx를 제외하겠다는 말).
*********************************************************************
**********************  APPLICATIONS INFORMATION  *******************
*********************************************************************
wx             : Can not link the wx driver, wx will NOT be useable

*********************************************************************
이대로 빌드하면 당연히 wx 관련 프로그램은 동작하지 않는다.

다음과 같이 관련 개발 패키지들을 설치하면 해결된다.
sudo apt-get install libwxbase2.8-dev libwxgtk2.8-dev libgl1-mesa-dev libglu1-mesa-dev libglut3-dev

NOTE: configure 설정에 cache 문제가 있는 듯하다. 필요한 패키지들이 최초의 configure를 실행하기전에 설치되어있어야한다. 그렇지 않을 경우에는 계속 wx 관련 링크못하겠다는 메시지가 뜨면서 wx를 build에서 제외하는 makefile을 만든다. 그러므로 기존 otp 디렉토리를 지우고, 원래 tar ball을 다시 압축을 풀어서 (fresh untar) 빌드해야한다.

NOTE2: configure 시에 --enable-smp-support 옵션을 꼭 줘야하고, 실행시에는 -smp 옵션을 주어야한다(erl -smp). 안 그러면 runtime에 다음과 같은 에러가 나면서 실행이 안된다.
WX ERROR: SMP emulator required** exception error: not_smp
     in function  wxe_server:start/0
     in call from wx:new/1
     in call from hello:start/0

참고:

happy hackin'

예전에 원작자인 ttyerl에게 패치를 보낸적이 있는데 적용되지 않았다. 그래서 그냥 포크했다.
또한 새로운 버그도 발견!!
davisp이 ttyerl의 원본을 좀 매끄럽게 돌게 고쳤는데 버그는 그대로였다.
그래서 davisp의 fork를 내가 fork해서 수정했다.

버그들:
  1. table_info 버그
  2. query timeout 버그

작업 내역:
  1. 상기 버그 퇴치
  2. eunit test case 추가


URL: http://github.com/mwpark/sqlite-erlang/tree/master

happy hackin'

erlyweb 빌드 하기

함수형 언어/Erlang 2009. 6. 1. 18:43 by manywaypark
erlyweb의 빌드 스크립트(make.sh)는 기본적으로 erlang의 lib 폴더(보통 /usr/local/lib/erlang/lib, code:lib_dir()로 찍어보면 나온다)에 yaws가 깔린 것으로 가정하고 만들어져있다.
즉, 다음과 같다.
#!/bin/bash

ERLIB=$(erl -noshell -eval 'io:format(code:lib_dir()).' -s erlang halt)
YAWS=$(ls $ERLIB | grep yaws)

cat >Emakefile <<EOF
{"src/erlyweb/*", [debug_info, {outdir, "ebin"},
{i,"$ERLIB/$YAWS/include"}]}.
{"src/erlydb/*", [debug_info, {outdir, "ebin"}]}.
{"src/erlsql/*", [debug_info, {outdir, "ebin"}]}.
{"src/erltl/*", [debug_info, {outdir, "ebin"}]}.
{"src/smerl/*", [debug_info, {outdir, "ebin"}]}.
{"src/erlang-mysql-driver/*", [debug_info, {outdir, "ebin"}]}.
{"src/erlang-psql-driver/*", [debug_info, strict_record_tests, {outdir,
"ebin"}]}.
EOF

ebin_dir="./ebin"
# bash check if directory exists
if [ ! -d $ebin_dir ]; then
    mkdir $ebin_dir
fi

erl -noshell -eval 'make:all(), filelib:fold_files("src/", ".+\.et$", true, fun(F, _Acc) -> erltl:compile(F, [{outdir, "ebin"}, debug_info, show_errors, show_warnings]) end, []).' -pa ebin -s erlang halt
일견 복잡해보이지만, 실제로 하는 일은 erlang의 기본 lib 폴더에서 yaws 폴더를 찾은 후에 Emakefile을 만들고 erlang 방식의 make를 돌리는 것이다.
문제는 나는 yaws를 직접 빌드해서 설치해서 다른 디렉토리(/usr/local/lib)에 있다는 것이다.
그냥 간단하게 make.sh를 다음과 같이 고쳐서 make 하면 잘된다. 나는 yaws의 기본 설정(즉, prefix 따위 설정 안한)으로 빌드했다. prefix 설정으로 설치 경로 바꾼 경우는 적절히 바꾸면 될 것이다.^^
#!/bin/bash

#ERLIB=$(erl -noshell -eval 'io:format(code:lib_dir()).' -s erlang halt)
#YAWS=$(ls $ERLIB | grep yaws)

cat >Emakefile <<EOF
{"src/erlyweb/*", [debug_info, {outdir, "ebin"},
{i,"/usr/local/lib/yaws/include"}]}.
{"src/erlydb/*", [debug_info, {outdir, "ebin"}]}.
{"src/erlsql/*", [debug_info, {outdir, "ebin"}]}.
{"src/erltl/*", [debug_info, {outdir, "ebin"}]}.
{"src/smerl/*", [debug_info, {outdir, "ebin"}]}.
{"src/erlang-mysql-driver/*", [debug_info, {outdir, "ebin"}]}.
{"src/erlang-psql-driver/*", [debug_info, strict_record_tests, {outdir,
"ebin"}]}.
EOF

ebin_dir="./ebin"
# bash check if directory exists
if [ ! -d $ebin_dir ]; then
    mkdir $ebin_dir
fi

erl -noshell -eval 'make:all(), filelib:fold_files("src/", ".+\.et$", true, fun(F, _Acc) -> erltl:compile(F, [{outdir, "ebin"}, debug_info, show_errors, show_warnings]) end, []).' -pa ebin -s erlang halt

happy hackin'

[BR] EUnit Bug Report

함수형 언어/Erlang 2008. 12. 24. 18:25 by manywaypark
맨 마지막 테스트 함수의 테스트 준비단계가 2번 실행되는 버그가 있는 것같다.
나의 test case가 성공할 때도 있고 실패할 때도 있는 이상한 상태가 되어서 내 잘못인 줄 알고 개발중인 모듈의 여기저기를 손댔다가 갑자기 느끼는 바가 있어 EUnit을 테스트하는 간단한 코드를 만들었다.

일단 공식적으로 버그 리포트는 했고,
참고를 위해 글을 남긴다.

last_test_() ->
do_some_prep(), % 요놈이 매번 두번씩 불린단 말씀.
... do some tests ...
do_some_prep()가 두번 불려지면 당혹스런(?) 테스트 실패가 나올 수 있다. disk i/o나 DB operation 처럼 side effect가 있는 경우에 특히 테스트가 통과될 때도 있고 안될때도 있는 불안정한 상태가 될 수 있다.

happy hackin'

ps. 글을 쓰고 좀더 만져 보니 조금 더 복잡하다. EUnit 사이트에 올린 케이스들 말고 테스트 케이스를 다음과 같이 하면...

twice_case1_test_() ->
    io:format("c1: prep one~n"),
    ?_assert(1 + 1 =:= 2).

twice_case2_test_() ->
    io:format("c2: prep one~n"),
    ?_assert(1 + 1 =:= 2).

twice_case3_test_() ->
    io:format("c3: prep one~n"),
    ?_assert(1 + 1 =:= 2).

결과:
(emacs@desktop)14> eunit_twice_case:test().
c3: prep one
c2: prep one
c3: prep one
c2: prep one
c1: prep one
  All 3 tests successful.
ok
마지막에 있는 두개가 두번씩 불린다.

2008-12-31 "by design" 이라는군요 Orz. side effect가 있는 함수는 준비단계에서 부르면 절대 안되겠습니다.
제목의 용어가 정확한지 잘 모르겠다. 작업도중 필요해서 만들게 되었는데 함수형 언어의 장점(REPL의 장점)을 부각시킬 수 있는 예제가 될 수 있을 것같아서 포스팅한다.
전각 문자를 반각으로 문자열 변환하는 간단한 함수를 하나 만들었는데 좀 꼼수(?)를 동원해서 얄팍하게 재빨리 만들었다.
정확하게 하려면 몇몇 자료를 참조해야하겠지만, 필요한 상황의 테스트 케이스는 다 통과하므로 그대로 쓰기로 했다.

(emacs@desktop)1> "ABC012". % wide chars
"\357\274\241\357\274\242\357\274\243\357\274\220\357\274\221\357\274\222".
[239,188,161,239,188,162,239,188,163,239,188,144,239,188,
 145,239,188,146]
(emacs@desktop)2> "A".         % wide char
"\357\274\241".
(emacs@desktop)3> "A".          % normal char
"A"
(emacs@desktop)4> "0".         % wide char
"\357\274\220".
[239,188,144]
(emacs@desktop)5> $0.           % normal char
48

패턴이 보이는가?
[239,188] ("\357\274") 뒤의 숫자만 변한다. ^^;
자연스럽게 다음과 같은 함수가 만들어진다.
%% 96 = last("1") - $1
'wide->narrow'([239,188,C|T]) ->
    [C - 96 | 'wide->narrow'(T)];
'wide->narrow'([C|T]) ->
    [C | 'wide->narrow'(T)];
'wide->narrow'([]) ->
    [].

결과:
(emacs@desktop)8> str_util:'wide->narrow'("ABC012").
str_util:'wide->narrow'("\357\274\241\357\274\242\357\274\243\357\274\220\357\274\221\357\274\222").
"ABC012"

REPL이 지원되지 않는 언어였더라면 과연 이렇게 눈깜짝할 사이에 만들 수 있었을까?
단위 작업의 feedback이 즉시 확인되는 것은 엄청난 매력이자 장점이다.

happy hackin'
좀 복잡한 테스트 케이스를 하나 추가하고 컴파일후 mod:test()를 실행했더니,
다음과 같은 에러가 나면서 그 모듈에 대한 유닛 테스트가 아예 동작하지 않았다.
*** test module not found ***
::false

{error,{module_not_found,false}}

순간 당황했으나, 방금 추가한 테스트 케이스를 살펴보니 _assert macro에 들어가는 내용이 잘못되어 있었다. boolean expression이 들어가야하는 곳에 그냥 expression만 하나 들어있었고, _assert(very_complex_expression) =:= expected value의 형태로 되어 있었다(어제 급히 추가하고 안돌려보고 바로 퇴근한 것이 후회되는 순간이었다). 그래도 컴파일은 멀쩡히 평소처럼 잘 되고 runtime에 가서야 에러가 나는 것은 좀 불편해보인다.

간략히 축약해서 예를 들자면 다음과 같다.
%% causes runtime error (see above msg).
?_assert(1+1) =:= 2
%% runs well w/o runtime error (good)
?_assert(1+1 =:= 2)

실제 내 코드에서는 1+1부분이 많이 복잡해서 화면을 넘어가고 있었고, _assert macro가 여러 개의 리스트로 묶여져 있어서 찾는데 좀 고생했다. Orz.

happy hackin'
1 2 3 4 
분류 전체보기 (306)
잡담 (20)
함수형 언어 (65)
Scheme (5)
Lisp (14)
Erlang (31)
R (3)
Elixir (11)
emacs (16)
java (18)
tips & tricks (154)
사랑 (1)
가사 (0)
독서 (4)
mobile (6)
비함수형 언어 (2)

공지사항

최근에 올라온 글

최근에 달린 댓글

최근에 받은 트랙백

05-07 05:13