Tag Archives: OCaml

2008년 1월 OCaml Meeting 프리젠테이션 & mp3

2008년 1월 26일 파리에서 OCaml 미팅이 있었나봅니다.

프리젠테이션과 mp3 파일이 올라왔네요. 평소에 관심만 있고 실제적으로는 활용을 아직 못하고 있는 언어인데… (LISP도 마찬가지) 여러가지로 관심이 가는 언어입니다. 요즘은 다이나믹 언어가 추세이긴 하지만, static type checking도 프로그램 안정성에 많은 기여를 하죠. Ocaml에서는 type inference 지원으로 자바처럼 열심히 type 선언을 안해줘도 됩니다. static type checking을 위해서 프로그래머가 노가다할 필요도 없고, 안전하면서도 유연한 소스가 나오죠… 언어적인 측면도 좋고, 컴파일러도 구현도 아주 잘 되어있지만, 라이브러리(특히 윈도우 관련 라이브러리)가 풍성하지 못해서 아직 활용을 못하고 있습니다.

아래 페이지에서 프리젠테이션과 mp3를 구할수 있습니다.
http://wiki.cocan.org/events/europe/ocamlmeetingparis2008

프랑스 파리에서 했는데, 모두 영어로 되어있는거 같긴 하네요. 다만 mp3가 너무 작게 녹음됐네요. mp3gain으로 볼륨 조절하시면 됩니다.

제가 요즘 실제로 사용하는 언어는:
   C++
   Python
   PHP
   Javascript
   Actionscript

관심있는 언어는:
   LISP
   Objective Caml
   D언어
   Smalltalk

입니다.

ocaml-event를 이용한 echo server

ocaml-event(libevent ocaml wrapper)를 이용하여 echo server를 OCaml 언어로 작성해 봤습니다. libevent C API에 익숙해서 ocaml 답지 않게 짜진거 같은 느낌도 들지만 워낙 프로그램이 간단하다보니 크게 틀려질 부분은 없어보이는군요. 다른건 문제가 없었는데 Makefile에서 ocamlopt로 native 컴파일할때 C 라이브러리가 링크가 안되서 약간 헤맸고, printf 후에 flush stdout을 하는 부분이 있는데, stdout이 Unix 네임스페이스에 있는 stdout으로 인식이 되어 컴파일이 안되더군요. 그래서 Pervasives.stdout으로 지정하니 컴파일 잘되네요.

아래는 echo_server_event.ml 소스입니다.

[CODE type=ocaml]
(*
* echo server using libevent
*
* Copyright (c) 2007 Chun-Koo Park
* All rights reserved.
*)

open Unix

let rec sock_write sock buf offset = function
  | 0 -> ()
  | len ->
     let nwritten = write sock buf offset len in
       sock_write sock buf (offset + nwritten) (len – nwritten)

let echo_callback event fd event_type =
  let buf = String.create 64 and nread = ref 1 in
   while !nread > 0 do
     nread := read fd buf 0 64;
     sock_write fd buf 0 !nread;
   done;
   if !nread > 0 then
     Libevent.add event None
   else
     Printf.printf “connection closed\n”;
     flush Pervasives.stdout

let accept_callback event fd event_type =
  let asock, addr = accept fd in
  let evnew = Libevent.create () in

  Libevent.set evnew asock [Libevent.READ] false (echo_callback evnew);
  Libevent.add evnew None;

  Libevent.add event None

let tcp_server_sock port =
  let ssock = socket PF_INET SOCK_STREAM 0
  and addr = inet_addr_any in
   bind ssock (ADDR_INET (addr, port));
   setsockopt ssock SO_REUSEADDR true;
   listen ssock 5;
   ssock

let _ =
  let listenport = 2007 in
  let acceptfd = tcp_server_sock listenport in
  let acceptev = Libevent.create () in

  Libevent.set acceptev acceptfd [Libevent.READ] false (accept_callback acceptev);
  Libevent.add acceptev None;

  Libevent.dispatch ()
[/HTML][/CODE]

ocaml-event를 이용한 echo 서버와 thread와 fork로 짠 echo 서버의 Makefile입니다. ocaml findlib를 이용했습니다. ocaml findlib에 대해서 얼마전에 올린글을 참고하세요.

[CODE type=makefile]
# Makefile
#
# Copyright (c) 2007 Chun-Koo Park
# All rights reserved.

OCAMLC=ocamlfind ocamlc
#OCAMLC=ocamlfind ocamlopt

EXEC=echo_server_fork echo_server_thread echo_server_event
all: $(EXEC)

echo_server_fork: echo_server_fork.ml
   $(OCAMLC) -o $@ -package unix -linkpkg $@.ml

echo_server_thread: echo_server_thread.ml
   $(OCAMLC) -o $@ -thread -package “threads unix” -linkpkg $@.ml

echo_server_event: echo_server_event.ml
   $(OCAMLC) -o $@ -package “libevent unix” -linkpkg $@.ml -ccopt -L/usr/local/lib -cclib -levent

clean:
   rm -f *.cmo *.cmi *.cmx *.o $(EXEC)

# vi:set noet:
[/HTML][/CODE]

thread 사용할때는 -thread 옵션을 주고, ocamlopt로 native 컴파일할 경우를 위해서 -cclib로 libevent C 라이브러리를 지정해주었습니다. -cclib event로 지정하면 라이브러리를 찾을거라 생각했는데 -cclib -levent 형태로 라이브러리 지정을 해야되더군요. ocamlc로 byte compile할때는 -cclib 를 지정하지 않아도 됩니다.

Ocaml로 작성한 세가지 echo server 소스입니다. ( libevent / fork / thread )

1402904692.tgz

ocaml findlib : ocamlfind

OCaml 언어에 예전부터 관심이 많았었는데, 오랜만에 다시 보니 새롭군요. 그전에 배우면서 CVS에 올렸던 날짜를 보니 2002년이네요. 5년이 지났지만 실전에 아직 OCaml를 쓸 기회가 없다보니 조금 안쓰다보면 까먹어서 자꾸 다시 배우게 되네요.

그 전에 배울때는 OCaml에 포함된 기본 컴파일러만 make와 병행해서 사용했었는데, ocaml-event 라이브러리를 깔다보니 ocamlfind라는 실행파일을 통해서 인스톨하게 되어 있더군요. ocaml-event는 libevent의 ocaml에서 사용할수 있도록 하는 라이브러리입니다. FreeBSD ports중에 /usr/ports/devel/ocaml-findlib를 깔고, ocaml-event는 수동으로 소스를 컴파일하여 인스톨했습니다. /usr/ports/devel/ocaml-event 에 있지만 여기서 인스톨하면 ocamlfind에 등록이 이상하게 안되더군요…

ocaml-findlib를 깔면 중심 실행파일이 ocamlfind인데, 이 파일이 상당히 다양한 기능들은 가지고 있습니다. Ocaml 라이브러리/프로그램 인스톨, 언인스톨, 컴파일, 소스에서 문서 추출, 라이브러리 종속성 조회, 라이브러리 브라우징 등의 기능을 제공하며, 브라우징의 경우 Tk를 이용하여 X11 환경에서만 실행됩니다. 자세한 것은 man 페이지나 findlib user guide 문서를 참고하세요.

저는 컴파일 할때 ocamlc나 ocamlopt를 바로 호출하지 않고 ocamlfind를 통해서 호출한 방법만 설명드리도록 하겠습니다. ocamlc는 자바처럼 byte code 형태로 컴파일되고, ocamlopt는 타겟 CPU에서만 실행되는 native 형태로 컴파일이 됩니다. 한 ocaml 프로그램 내에서 byte code와 native code로 컴파일된것을 링크할수 없으므로 Makefile를 이용한다면 이를 따로따로 관리해야합니다. ocamlfind를 이용하면 cmo(byte code object)나 cmx(native object)를 지정하지 않아도 되서 Makefile의 크기가 많이 줄어드는것 같네요. 라이브러리 지정도 cma, cmxa를 따로 지정하지 않으며, 또한 라이브러리의 종속성을 자동으로 판단하여 라이브러리 링크 순서도 자동으로 판단해주는거 같습니다.

예전에 짰던 echo_server fork버전과 thread버전의 Makefile.old와 변경된 Makefile.new을 비교해보시면 ocamlfind가 유용하다는걸 느끼실수 있을겁니다. 그리고 Makefile.old는 native로 컴파일할려면 많은 부분 고쳐야하지만, Makefile.new에서는 한줄만 바꾸면 됩니다.

[CODE type=make]
# Makefile.old
# Copyright (c) 2002 Chun-Koo Park

OCAMLC=ocamlc
OCAMLCFLAGS=-thread
.SUFFIXES: .ml .mli .cmo .cmi

LIBS=unix.cma

EXEC=echo_server_fork echo_server_thread
all: $(EXEC)

clean:
   rm -f *.cmo *.cmi *.cmx *.o $(EXEC) .depend

.mli.cmi:
   $(OCAMLC) $(OCAMLCFLAGS) -c $<

.ml.cmo:
   $(OCAMLC) $(OCAMLCFLAGS) -c $<

echo_server_fork: $@.cmo $(OBJS)
   $(OCAMLC) -o $@ $(OCAMLCFLAGS) $(LIBS) $@.cmo

echo_server_thread: $@.cmo $(OBJS)
   $(OCAMLC) -thread -o $@ $(OCAMLCFLAGS) unix.cma threads.cma $@.cmo

# vi:set noet:
[/HTML][/CODE]

[CODE type=make]
# Makefile.new
# Copyright (c) 2007 Chun-Koo Park

OCAMLC=ocamlfind ocamlc
#OCAMLC=ocamlfind ocamlopt

EXEC=echo_server_fork echo_server_thread
all: $(EXEC)

echo_server_fork: echo_server_fork.ml
   $(OCAMLC) -o $@ -package unix -linkpkg $<

echo_server_thread: echo_server_thread.ml
   $(OCAMLC) -o $@ -thread -package “threads unix” -linkpkg $<

clean:
   rm -f *.cmo *.cmi *.cmx *.o $(EXEC) .depend

# vi:set noet:
[/HTML][/CODE]

위 Makefile 들은 FreeBSD에서 BSD Parallel Make에서 테스트된 파일들입니다. 마지막 줄은 vim에서 탭을 자동으로 공백으로 확장하지 않도록 하는 명령입니다. (noexpandtab)

findlib에 포함된 make_wizard를 사용하면 위저드 형태로 7단계로 Makefile를 만들어줍니다. 생성되는 Makefile이 상당히 길고, 생성된 Makefile은 위저드로 편집이 불가능하기 때문에 사용이 꺼려지긴 합니다. 하지만, 참고할만한 마땅한 Makefile이 별로 없었는데 소스만 봐도 큰 도움이 될듯하네요. 다음은 make_wizard를 실행한 화면입니다.

윈도우즈에서 OCaml 개발 환경 (tuareg)

예전엔 FreeBSD에서 vi로 작업했었는데, Lisp/Slime 환경에 친숙해지면서 OCaml에서도 비슷한 개발환경을 찾아보게 됐습니다. Emacs외에도 몇가지 통합개발환경이 있지만 저는 Emacs 위에서 돌아가는 tuareg mode를 받아서 설치했습니다. 저는 로컬 윈도우즈에서 Emacs를 사용하지만 유닉스에서도 동일하게 사용할수 있습니다. OCaml은 mingw 기반의 설치파일을 받아서 설치했습니다.

tuareg는 tuareg-mode-1.46.1.zip를 받아서 Emacs의 site-lisp에 압축을 풀었습니다. 그리고는 다음을 .emacs 파일에 추가해주었습니다.

[CODE type=lisp](add-to-list ‘load-path “c:/Progra~1/emacs/site-lisp/tuareg-mode-1.46.1”)
(setq auto-mode-alist (cons ‘(“\\.ml\\w?” . tuareg-mode) auto-mode-alist))
(autoload ‘tuareg-mode “tuareg” “Major mode for editing Caml code” t)
(autoload ‘camldebug “camldebug” “Run the Caml debugger” t)

(defvar tuareg-interactive-program “c:/Progra~1/Object~1/bin/ocaml”)
(defvar tuareg-library-path “c:/Program Files/Objective Caml/lib”)
[/HTML][/CODE]
tuareg-interactive-program 지정할때  Program Files와 Objective Caml등으로 넣으면 OCaml 실행이 안되더군요.. 위 사항을 적용하면 ml이나 mli 파일을 읽을때 tuareg-mode로 자동으로 넘어갑니다.

키는 Lisp/Slime과 많이 유사하지만, Slime보다는 제공하는것이 적네요. C는 Ctrl, M은 Alt(Meta)키 입니다.

  • C-x C-e, C-c C-e, C-M-x : 세가지 키조합 모두 현재 expression을 실행합니다. OCaml이 실행중이 아니면 실행합니다.
  • C-c C-b : 현재 버퍼(파일)을 실행합니다.
  • C-c C-c : make를 실행하는데 프로젝트에 맞는 Makefile이 있어야합니다.
  • C-M-p, C-M-n : (toplevel) expression 위아래로 이동.
  • C-c C-a : ml과 mli 사이를 왔다갔다 합니다. 아주 유용한 기능이네요.

또 Emacs메뉴에 Tuareg에 보면 여러가지 기능이 제공됩니다. Definition-Scan을 선택하면 현재 파일에서 모든 정의(type & value)를 정리해서 메뉴에 보여줍니다. 또 C-c . 으로 시작하는 명령들이 있는데, 많이 사용하는 구절들을 빠르게 입력할수 있게 도와줍니다. 한가지 아쉬운점은 completion 기능이 동작하지 않는데, 왜 그런지 아직 파악하지 못했습니다. 메뉴에 보면 관련 항목이 있는데, 활성화 되어있지 않습니다. 또한 키도 할당되어 있지 않습니다. 소스를 보면 C-c TAB( (define-key map [?\C-c ?\t] ‘tuareg-complete) 로 할당되어 있는데, 이 키는 Interrupt Caml Toplevel로 할당되어 보이고, M-x tuareg-complete로 호출해도 “Symbol’s function definition is void: caml-complete”라는 메시지만 출력됩니다.

좀더 연구 해봐야겠습니다…

Unison을 이용한 로컬 백업

Objective Caml에 평소에 관심이 많다보니, O’Caml로 짠 유명한(?) 프로그램도 관심있게 지켜보고 있습니다. mldonkeyunison이 그래도 많이 쓰이는 프로그램인 거 같은데, mldonkey는 아직 사용은 못해봤고, Unison은 전에 설치는 해봤는데 사용은 제대로 못했었습니다. mldonkey는 외국에서 많이 사용하는 P2P 프로토콜(edonkey, fasttrack, bittorrent, gnutella, dc…)을 모두 지원하는 텍스트 기반 P2P 프로그램입니다. Unison은 두 디렉토리들을 동기화시켜주는 유틸리티입니다. 버전 관리툴하고 유사하긴 하지만 버전을 관리해주지는 않고 양쪽 디렉토리에서 변경되는 부분을 양방향으로 동기화 해줍니다. 주로 원격 컴퓨터간 동기화하기 위해 만들어진것 같지만 한 컴퓨터 내에서도 아주 잘 동작합니다. 원격 컴퓨터간 동기화 할때는 ssh, rsh와 unison 자체 프로토콜 등을 지원합니다.

회사의 모든 소스는 버전관리툴로 관리합니다. 하지만, 집에서 사용중인 많은 개인 파일들을 따로 등록해서 관리하기는 용량도 좀 크고, CD나 DVD로 백업하기도 귀찮더군요. 백업 하드디스크 따로 사서 가끔씩 중요한 폴더들을 그대로 복사하다가 시간도 오래 걸리고, 까먹고 해서 백업 툴들을 찾아봤었는데, 괜찮은것들은 대부분 쉐어웨어더군요. 음악을 취미로 하다보니 녹음한번 하면 꽤 용량이 됩니다. 그리고 새로 업데이트된 폴더들을 찾아서 복사해주는것도 일이더군요. 전체 다 복사하면 너무 오래걸리고요…

Unison은 알고있었는데 원격 동기화 프로그램으로만 생각해서 로컬 백업에 적합하다고 생각을 못했었습니다. 사용해보니, 사용방법도 간단하고 한번 세팅해두니 매일 새벽 자동으로 백업이 됩니다. ^^

unison win32 바이너리unison-2.13.16-win-text.exe를 받았습니다. 유닉스하고 버전을 맞추기 위해서 stable 버전을 받았습니다. 그래픽 버전은 GTK도 설치해야하고 백업은 사용자 인터액션 없이 자동으로 실행되는게 편하다고 생각하여 텍스트 버전으로 받았습니다. 적당한 디렉토리에 복사하고 커맨드창에서 다음을 실행하면 됩니다. 실행 파일명은 간단하게 바꿨습니다. 백업 디렉토리 f:\backup\music은 미리 만들었습니다.

unison.exe -batch -fastcheck true c:\music f:\backup\music

-batch 옵션은 실행도중에 디폴트로 자동응답하도록 하며, -fastcheck true는 양쪽 파일들을 비교할때 전체 파일에 대한 해시(핑거프린트)를 바로 하지 않고, 변경날짜 등으로 비교한후 틀린 파일들에 대해서 해시비교한다고 합니다. -fastcheck true 옵션을 주지 않고 대용량 디렉토리를 하면 무지 느립니다! 그리고, 처음 실행할때는 원래 느립니다. (모든 파일에 대해서 해시하는거 같네요.)

하지만 위에 처럼 실행하면 양쪽 디렉토리를 동기화해주기 때문에 backup 디렉토리에서 파일을 삭제하면 원본에서도 지워집니다. 그래서 한방향으로만 씽크해주는 옵션이 있습니다.
-force ROOTDIR로 어느쪽이 원본인지를 알려주면 됩니다.

unison.exe -batch -fastcheck true -force c:\music c:\music f:\backup\music

백업을 원하는 디렉토리 백업하는 batch 파일을 위에처럼 만들어주고, “시작-프로그램-보조프로그램-시스템도구-예약된작업” 에 등록시켜서 매일 새벽에 실행시켜주면 백업 걱정 끝입니다. 물론 가끔씩 DVD나 네트워크 백업을 해주는게 더 안전하겠죠.

백업을 열심히 하는 편인데… CD도 믿을만한 매체는 못되는거 같더군요. 전에 백업해 둔 CD를 못읽어서 6개월 정도의 데이타를 날린적이 있습니다. TT