모듈은 자바에서의 'class' 개념과 유사하다.
각 모듈은 데이터(변수)와 함수들로 구성된다.
모듈은 type이 아니기 때문에, 모듈은 인스턴스화(Instantiate)할 수 없다.
// main.ml
let _ =
let _ = Format.printf "Result: %d\n" Operation.def_val in
Format.printf "Result %d\n" (Operation.add 3 7)
// operation.ml
let def_val = 99
let add x y = x + y
위 예시에서 main, operation 두 파일 모두 모듈이다.
모듈에 정의된 변수나 함수는
[module_name].[var_name]
으로 접근할 수 있다.
이렇게 접근하기 위해서 모듈의 이름은대문자
가 된다.
operation.ml 파일이면, Operation.add 로 참조해야 한다.
자바, C++에서의 inner class 개념과 유사하다.
🌱 Nested Module 정의
module [module_name] = struct [defs] end
이다.
module_name은대문자
로 시작해야 한다.🌱 Nested Module 접근법
[module_name].[nested_module_name].[var_name]
이다.
// main.ml
let _ =
let _ = Format.printf "Result: %d\n" Operation.IntOp.add in
Format.printf "Result %d\n" (Operation.FloatOp.add 3.0 7.0)
// operation.ml
module IntOp = struct
let add x y = x + y
end
module FloatOp = struct
let add x y = x + y
end
open [module_name]
으로 작성하며, 모듈의 definition을 import한다.
// Operation을 omit
open Operation
let _ =
let _ = Format.printf "Result: %d\n" IntOp.add in
Format.printf "Result %d\n" (FloatOp.add 3.0 7.0)
// Operation, IntOp omit
open Operation
open IntOp
let _ =
let _ = Format.printf "Result: %d\n" add in
Format.printf "Result %d\n" (FloatOp.add 3.0 7.0)
// operation.ml
module IntOp = struct
let add x y = x + y
end
module FloatOp = struct
let add x y = x + y
end
open Operation
open IntOp
open FloatOp
let _ =
let _ = Format.printf "Result: %d\n" add in // FloatOp.add
Format.printf "Result %d\n" (add 3.0 7.0) // FloatOp.add
이 경우, add 함수가 IntOp와 FloatOp 모두에 정의되어 있기 때문에 이 경우 conflict가 발생한다.
이 때 마지막으로 Open된 FloatOp의 함수 FloatOp.add가 불러진다.
그러나 컴파일러는 FloatOp.add 에 integer 값인 3 과 7을 전달하였기 때문에 에러를 발생시킬 것이다 !
module [abbreviation] = [module_name]
으로, 축약형 표현으로 Module을 참조할 수 있다.
Module OI = Operation.IntOp
Module F = Format
let _ = F.printf "Result %d\n" (OI.add 3 7) in
let _ = F.printf "Result %d\n" (OI.add 1 4) in
let _ = F.printf "Result %d\n" (OI.add 4 12) in
F.printf "Result: %d\n" (OI.add 2.5)
모듈을 specific scope에서 open할 수도 있다.
let open [module_name] in [expression]
이전의 경우는 global scope에서 open한 것이라면, 아래 예시는 specific scope에서 module을 open한 것이다.
let _ =
let open Operation.IntOp in
let _ = F.printf "Result %d\n" (OI.add 3 7) in
let _ = F.printf "Result %d\n" (OI.add 1 4) in
let _ = F.printf "Result %d\n" (OI.add 4 12) in
F.printf "Result: %d\n" (OI.add 2.5)
자바의 switch 와 비슷하지만, 더욱 더 강력한 도구이다.
match expression with
| pattern1 -> expression1
| pattern2 -> expression2
...
| patternN -> expressionN
module F = Format
module Fib = struct
let rec fib i =
match i with
| 0 -> 0
| 1 -> 1
| n -> fib (n-2) + fib(n-1)
end
let _ =
let _ = F.printf "Res: %d\n" (Fib.fib 0) in
let _ = F.printf "Res: %d\n" (Fib.fib 1) in
let _ = F.printf "Res: %d\n" (Fib.fib 2) in
F.printf "Res: %d\n" (Fib.fib 3)
let _ =
let even_or_odd i =
match i mod 2 with
| 0 -> F.printf "Even\n"
| 1 -> F.printf "Odd\n"
| _ -> F.printf "Unknown\n"
in
let _ = even_or_odd 0 in
let _ = even_or_odd 1 in
let _ = even_or_odd 2 in
even_or_odd 3
언더바는 와일드 카드로, 어떤 값이든 올 수 있다. 다만 항상 0 혹은 1일 것이므로 이 부분은 필요 없다!
그러나 언더바 부분을 삭제한다면, Ocaml 컴파일러는 에러를 발생시킨다. 패턴매칭이 만족스럽지 않은 이유로 2를 예시로 든다.
우리는 사람으로서 2는 i mod 2 의 결과값이 0 혹은 1인것을 알지만, Ocaml 컴파일러는 알지 못한다.Ocaml 컴파일러는 ❌expression의 결과값❌이 아닌, ⭕expression의 type⭕으로 패턴 매칭의 완벽함을 체크한다.
그러나 "i mod 2" 의 evaluation result가 integer type이기 때문에, Ocaml은 패턴 매칭이 모든 가능한 정수값(0, 1, 2, 3, ..)을 포함하고 있는지 체크한다. 따라서 2는 커버되지 않고 있으므로 에러를 발생시키는 것이다.
따라서 이 wildcard는 필요하다 !
연속적인 문자나 숫자를 가리키기 위해서 .. 을 사용할 수 있다 !
let _ =
let is_lowercase c =
match c with
| 'a' .. 'z' -> true
| _ -> false
in
let _ = F.printf "A: %b \n" (is_lowercase 'A') in (*false*)
let _ = F.printf "A: %b \n" (is_lowercase 'W') in (*false*)
let _ = F.printf "A: %b \n" (is_lowercase 'b') in (*true*)
F.printf "A: %b \n" (is_lowercase 'z') (*true*)
let _ =
let is_lowercase c =
match c with
| 'a' .. 'z' -> true
| _ -> 0
in
let _ = F.printf "A: %b \n" (is_lowercase 'A') in (*false*)
let _ = F.printf "A: %b \n" (is_lowercase 'W') in (*false*)
let _ = F.printf "A: %b \n" (is_lowercase 'b') in (*true*)
F.printf "A: %b \n" (is_lowercase 'z') (*true*)
따라서 아래와 같은 경우 전자는 true를, 후자는 0을 반환하므로 type이 달라서 ocaml compiler는 에러를 발생시킨다.
let _ =
let get_fst p =
match p with
| (fst, _) -> fst
in
let get_snd p =
match p with
| (_, snd) -> snd
in
let _ = F.printf "fst: %d\n" (get_fst (1,3)) in (*1*)
let _ = F.printf "fst: %d\n" (get_fst (2,4)) in (*2*)
let _ = F.printf "fst: %d\n" (get_snd (1,3)) in (*3*)
F.printf "fst: %d\n" (get_snd (2, 4)) (*4*)
let _ =
let calc tup =
match tup with
| ('+', x, y) -> x + y
| ('-', x, y) -> x-+ y
| ('*', x, y) -> x * y
| ('/', x, y) -> x / y
| _ -> failwith "not Supported yet"
in
let _ = F.printf "CalcRes : %d\n" (calc('+', 1, 3)) in (*4*)
let _ = F.printf "CalcRes : %d\n" (calc('+', 4, 2)) in (*6*)
let _ = F.printf "CalcRes : %d\n" (calc('-', 1, 3)) in (*-2*)
let _ = F.printf "CalcRes : %d\n" (calc('-', 4, 2)) in (*2*)
let _ = F.printf "CalcRes : %d\n" (calc('*', 1, 3)) in (*3*)
let _ = F.printf "CalcRes : %d\n" (calc('*', 4, 2)) in (*8*)
let _ = F.printf "CalcRes : %d\n" (calc('/', 1, 3)) in (*0*)
let _ = F.printf "CalcRes : %d\n" (calc('/', 4, 2)) in (*2*)
F.printf "CalcRes : %d\n" (calc('%', 1, 3)) in (*Failure*)