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

[펌] Erlang BIF 만들기

함수형 언어/Erlang 2013. 11. 9. 22:33 by manywaypark

steps

1. run configure
2. add your bifs to erts/emulator/beam/bif.tab

bif re:grep/2 bif re:compile/1

3. create a C file

erts/emulator/beam/erl_bif_re.c

4. add your C file to erts/emulator/<arch>/Makefile

RUN_OBJS = $(OBJDIR)/erl_bif_re.o \

5. implement your bifs by stealing bits from existing erl_bif_*.c files
1
2
3
4
5
BIF_RETTYPE re_grep_2(BIF_ALIST_2){
  Eterm result;
  result = magic_function();
  BIF_RET(result);
}
6. run make; make install

notes

  • steps 0-3 need only be done once.
  • note that if you add
1
bif re:grep/2

to bif.tab there should be a erl_bif_re.c that implements

1
BIF_RETTYPE re_grep_2(BIF_ALIST_2);


from: https://erlangcentral.org/wiki/index.php/Adding_my_own_BIF


happy hackin'

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'

LangDev 발표 자료

카테고리 없음 2009. 12. 12. 15:25 by manywaypark
발표 장소에서 완성 하다. Orz.
예제 위키 서버 erlki는 미완임.

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가 있는 함수는 준비단계에서 부르면 절대 안되겠습니다.
1 2 3 4 
분류 전체보기 (306)
잡담 (20)
함수형 언어 (65)
emacs (16)
java (18)
tips & tricks (154)
사랑 (1)
가사 (0)
독서 (4)
mobile (6)
비함수형 언어 (2)

공지사항

최근에 올라온 글

최근에 달린 댓글

최근에 받은 트랙백

05-01 19:56