OCaml 알아보기

이현재·2025년 1월 19일
0

코드를 작성하다 보면, 새로운 언어에 대한 갈증이 생길 때가 있습니다. 특히 TypeScript처럼 정적 타입을 사용하는 언어에 익숙해진 분이라면, 함수형 프로그래밍의 강점을 더 극대화할 수 있는 다른 언어가 궁금해질 수도 있죠.
그런 맥락에서 OCaml은 “함수형 언어의 정수를 빠르게 경험할 수 있는 언어”라고 소개할 수 있습니다. 1970~80년대부터 이어진 ML 계열의 전통을 잇고 있으며, 최근에는 버전 5.x에서도 병행성(Concurrency) 지원을 강화하는 등 꾸준히 발전하고 있습니다.

이 글에서는 OCaml을 처음 접할 때 알아두면 좋은 문법과 개념들을 TypeScript와 비교하면서 살펴보겠습니다.

OCaml 한눈에 보기

  1. 함수형 프로그래밍(FP)에 최적화: 커링, 부분 적용 등 FP 개념을 언어 차원에서 지원
  2. 강력한 타입 추론: TypeScript가 제공하는 타입 추론보다 더욱 세밀하고 편리한 추론 능력을 보여줌
  3. ADT(Algebraic Data Type)와 패턴 매칭: match 문법과 함께 복잡한 자료 구조를 간결하게 다룰 수 있음
  4. 네이티브 컴파일/바이트코드 컴파일 지원: 네이티브 바이너리로 컴파일해 빠른 실행 속도 확보 가능
  5. 풍부한 모듈 시스템 및 패키지 관리(OPAM): 규모가 큰 프로젝트도 체계적으로 관리 가능

정리해 보면, OCaml은 정적 타입 기반의 함수형 언어로서의 장점을 극대화하고, 운영 환경에서도 유연한 선택지를 제공하는 언어입니다.

개발 환경 준비하기

1) OPAM과 OCaml 설치

  • OPAM은 OCaml 생태계에서 표준으로 쓰이는 패키지 매니저입니다.
  • MacOS 기준으로 Homebrew를 통해 간편하게 설치할 수 있습니다.
brew install opam
opam init
eval $(opam env)

이후 원하는 버전의 OCaml 컴파일러를 설치합니다.

opam switch create 4.14.0
eval $(opam env)

Visual Studio Code에서 OCaml Platform 확장을 설치하면 편리하게 코드 작성 및 타입 검사 등이 가능합니다.

2) REPL 사용하기: utop

  • TypeScript에서 ts-node를 사용할 수 있듯, OCaml에선 utop이 있습니다.
opam install utop
utop

실시간으로 코드를 실행하고, 결과와 타입을 살펴볼 수 있어서 OCaml 학습 초기엔 필수적으로 쓰이게 됩니다.

주요 언어 특징 & 문법 훑어보기

1) let과 불변성(immutability)

OCaml에서 값을 정의할 때는 let 키워드를 사용합니다.

let x = 10
let y = x + 20
let msg = "Hello OCaml"
  • 이러한 변수들은 기본적으로 불변(immutable)입니다. “값을 정의한다”라는 개념에 더 가깝다고 볼 수 있습니다.
  • 필요하다면 ref 등을 통해 가변성을 줄 수 있지만, 함수형 언어의 장점을 살리기 위해서는 불변 패턴을 최대한 유지하는 편이 좋습니다.

TypeScript 비교 TS에서도 const를 사용하면 재할당이 불가능하지만, 내부적으로는 배열 등을 통해 얼마든지 수정할 수 있죠. OCaml 리스트는 아예 구조적 불변을 권장하는 특성이 더 강합니다.

2) 타입 추론과 명시

OCaml은 타입을 상당히 엄격하게 다루지만, 동시에 타입 추론이 강력해서 대부분의 경우 타입 표기를 생략해도 정확히 추론해줍니다.

(* 추론에 의존 *)
let add a b = a + b

(* 명시적 타입 선언 *)
let add_explicit (a : int) (b : int) : int =
  a + b

TypeScript와 달리, 함수의 파라미터를 한 번에 전달하기보다 순차적으로(커링) 넘기도록 설계돼있습니다.

함수형스러운 요소: 커링과 패턴 매칭

1) 고계 함수와 커링

OCaml에서 함수는 1급 시민(First-class citizen)입니다. 다른 함수에 인자로 넘길 수 있고, 반환값으로도 사용할 수 있죠.

let apply_twice f x =
  f (f x)

let inc n = n + 1
let result = apply_twice inc 5  (* 7 *)

또한 기본적으로 커링이 적용되기 때문에, f(a, b) 형태가 아니라 f a b(혹은 f a (b))로 보는 개념이 강합니다. 이를 통해 부분 적용(Partial Application)도 매우 간단해집니다.

let add a b = a + b
let plus_ten = add 10
let r = plus_ten 5  (* 15 *)

2) 패턴 매칭(Algebraic Data Type)

OCaml이 자랑하는 강력한 기능 중 하나는 알제브라적 자료형(ADT)과 패턴 매칭입니다.

type shape =
  | Circle of float
  | Rectangle of float * float

let area s =
  match s with
  | Circle r -> 3.14 *. r *. r
  | Rectangle (w, h) -> w *. h

TypeScript로 치면 유니온 타입(kind: 'circle' | 'rectangle')을 만들어 switch 구문으로 분기 처리하는 방식과 유사하지만, OCaml의 패턴 매칭 문법은 간결함과 동시에 철저한 타입 안전성을 제공합니다.

자료 구조 맛보기

1) 레코드(Record)

OCaml의 레코드는 이름과 타입이 지정된 필드를 갖는 구조체 같은 개념입니다.

type person = {
  name: string;
  age: int;
}

let me = { name = "Alice"; age = 30 }

TypeScript의 type 혹은 interface와 비슷하지만, 필드를 변경하는 것은 record를 새로 만드는 식으로 처리하는 경우가 많습니다(불변성).

2) 리스트와 배열

리스트

기본 자료 구조이며 불변입니다. 연결 리스트 구조로, head :: tail 패턴 매칭이 자주 사용됩니다.

let nums = [1; 2; 3]
let chars = ['a'; 'b'; 'c']

배열

가변(mutable)을 허용하며, 인덱스로 접근합니다.

let arr = [| 1; 2; 3 |]
arr.(0) <- 10  (* 가능, 값이 10으로 변경 *)

TypeScript에서 const arr = [1, 2, 3]는 당연히 가변적인 구조지만, OCaml에서는 명확히 리스트(불변)배열(가변)을 구분하여 선택적으로 사용할 수 있습니다.

예외와 에러 처리

1) 예외(Exception)

OCaml도 try ... with ... 형태의 예외 처리를 지원합니다.

exception NegativeValue of string

let risky x =
  if x < 0 then raise (NegativeValue "Negative not allowed")
  else x

let () =
  try
    print_int (risky (-1))
  with
  | NegativeValue msg -> print_endline ("Caught: " ^ msg)

2) Option / Result

함수형 언어답게, 명시적인 오류 처리를 위해 option(Some, None) 또는 result(Ok, Error) 타입을 즐겨 사용합니다.

let safe_div x y =
  if y = 0 then None
  else Some (x / y)

match safe_div 10 0 with
| Some r -> Printf.printf "Result: %d\n" r
| None   -> Printf.printf "Cannot divide by zero\n"

TypeScript에서도 null, undefined, 또는 직접 정의한 Result 타입으로 비슷하게 처리할 수 있지만, OCaml은 해당 패턴이 언어적 관습으로 자연스럽게 정착되어 있다는 점이 특징입니다.

빌드, 실행, 프로젝트 관리

1) 단일 파일 컴파일

바이트코드

ocamlc -o main main.ml
./main

네이티브

ocamlopt -o main main.ml
./main

2) 규모 있는 프로젝트: dune

dune은 OCaml 공식 빌드 시스템입니다.

opam install dune
dune init proj my_ocaml_project
dune build
dune exec ./my_ocaml_project.exe

의존성 관리, 테스트, 여러 파일/라이브러리 연결 등 대규모 프로젝트를 편리하게 관리해줍니다.


OCaml은 “함수형 패러다임이란 이런 것이구나”를 제대로 체감하게 해주는 언어이자, 타입 안전성과 런타임 성능을 놓치고 싶지 않은 개발자에게 매력적인 선택지입니다.

  • TypeScript 개발자에게 익숙한 점
  • 정적 타입, 모듈 시스템, 부분적으로 유사한 함수 문법
  • 차별화된 강점
  • 언어적 차원에서 지원하는 패턴 매칭, 강력한 커링/부분 적용, 고도의 타입 추론
  • 네이티브 바이너리로 컴파일 가능한 확장성

처음에는 다소 생소할 수 있지만, utop으로 간단한 코드를 실험해보는 것부터 시작해보세요.
조금씩 리스트를 다루고, 패턴 매칭으로 ADT를 분기 처리하다 보면, 분명 TypeScript와는 또 다른 함수형 세계를 즐기게 되실 겁니다.

profile
코드 보는걸 좋아합니다. 궁금한게 많습니다.

0개의 댓글