OCaml 5.5.0 버전 출시 안내
요약
OCaml 5.5.0 버전이 출시되었으며, 주요 업데이트로는 모듈 의존 함수 지원, 이동 가능한 컴파일러 기능 추가 등이 있습니다. 이를 통해 개발자는 더 유연하고 효율적인 코드를 작성할 수 있게 되며, 특히 대규모 프로젝트의 빌드 및 디버깅 과정에서 큰 이점을 얻을 것으로 기대됩니다.
핵심 포인트
- 모듈을 경량 함수자 형태로 인수로 사용 가능해져 코드 유연성이 높아졌습니다.
- 이동 가능한 컴파일러 기능으로 로컬 opam 스위치 생성 시간이 단축되었습니다.
- 고차 다형 함수를 직접 함수 인수로 정의할 수 있게 되어 코드가 간결해졌습니다.
Blaise Pascal의 생일을 기념하며 OCaml 버전 5.5.0의 출시를 발표하게 되어 기쁩니다.
OCaml 5.5.0의 주요 하이라이트는 다음과 같습니다:
모듈 의존 함수 (Module-dependent Functions)
이제 모듈을 경량 함수자(lightweight functors) 형태로 함수 인수로 사용할 수 있습니다.
예를 들어, Map.Make 함수자를 통해 생성된 맵을 출력하는 함수를 정의할 수 있습니다:
let pp_map (module M: Map.S) pp_key pp_v ppf set =
if M.is_empty set then
Format.fprintf ppf "ø"
...
그리고 이 함수를 문자열 맵에 적용할 수 있습니다:
module String_map = Map.Make(String)
다음과 같이 사용합니다:
let () =
let m = String_map.of_list ["Zero", "Zero"; "One", "Un"] in
let pp_str = Format.pp_print_string in
...
첫 번째 클래스 모듈(first-class modules)과 비교했을 때, pp_map 함수의 타입은 다음과 같습니다:
type 'a printer = Format.formatter -> 'a -> unit
val pp_map: (module M: Map.S) -> M.key printer -> 'a printer -> 'a M.t printer
이 타입은 모듈 S의 값에 의존적(dependent over the value of the module S)이며, 따라서 함수는 정적으로 알려진 모듈에 대해서만 적용될 수 있습니다:
let f (): (module Map.S) =
if Random.bool () then
(module Map.Make(Int))
...
Error: This expression has type
(module M : Map.S) ->
(Format.formatter -> M.key -> unit) ->
...
이동 가능한 컴파일러 (Relocatable Compiler)
컴파일러 설치를 이제 호환되지 않는 바이트코드 런타임 인터프리터를 혼합하여 디버깅하기 어려운 오류가 발생할 위험 없이 이동하거나 복사할 수 있습니다.
실질적으로 이는 전역 스위치(global switch)와 동일한 컴파일러 버전 및 구성이 사용 가능한 경우, 전체 컴파일러를 재컴파일하는 대신 글로벌 스위치를 클론하여 로컬 스위치를 생성할 수 있음을 의미합니다.
이는 새로운 로컬 opam 스위치를 즉시(out-of-the-box) 생성하는 데 필요한 시간을 상당히 줄여줄 것입니다.
함수 인수로 사용되는 다형 함수 (Polymorphic Functions as Function Arguments)
고차 다형 함수(Higher-rank polymorphic functions)를 이제 함수 인수를 사용하여 직접 정의할 수 있습니다.
let apply_map (map: 'a 'b. ('a -> 'b) -> 'a list -> 'b list) =
map string_of_int [1;2;3], map List.singleton ["x"; "y"]
let _ = apply_map List.map
이전에 이러한 함수를 정의하려면 레코드(record) 또는 다음을 가진 객체(object)를 거쳐야 했습니다:
type map = { map: 'a 'b. ('a -> 'b) -> 'a list -> 'b list }
let apply_map {map} =
map string_of_int [1;2;3], map List.singleton ["x"; "y"]
부분 문자열 검색 및 대체 함수 (Search and Replace Substring Functions)
String 모듈이 문자열 내부의 부분 문자열을 검색하고 대체하기 위한 많은 함수로 확장되었습니다.
let _true = String.includes ~affix:"aba" "abbaba"
let sentence = String.replace_all ~sub:"𝄽" ~by:"word" "A 𝄽 is re𝄽ed"
부분 문자열 검색은 2방향(2-way) 문자열 매칭 알고리즘을 사용하며, 이는 탐지할 문자열 크기와 관계없이 상수 공간 메모리 오버헤드를 요구한다는 장점이 있습니다.
일반화된 지역 정의 (Generalised Local Definitions)
이제 타입(type), 클래스(class), 모듈 타입(module type) 또는 전역적으로 정의될 수 있는 모든 종류의 항목을 로컬로 항상 정의할 수 있게 되었습니다:
let mandelbrot n x =
let type t = Converge | Escape of int in
...
...
외부 타입 (External Types)
외부 함수 라이브러리(foreign function libraries)와 인터페이스할 때, 이제 외부 타입을 정의하는 것이 가능해졌습니다:
type int_gmp = external "mpz_t"
type float_gmp = external "mpf_t"
추상 타입 정의(abstract type definition)와 비교했을 때, 외부 타입 이름인 “mpz_t”(resp. mpf_t)는 해당 타입을 어떤 비추상 타입이나 다른 이름을 가진 외부 타입과 구별할 수 있게 합니다.
특히, 이는 일반화된 추상 데이터 타입(Generalised Abstract Data Types, GADTs)과 결합될 때 FFI 타입의 동작을 더 좋게 만듭니다. 예를 들어, 타입 체커는 다음이 총 함수(total function)임을 증명할 수 있습니다:
let ok: (int_gmp,[` A] ) Type.eq -> _ = function _ -> .
왜냐하면 외부 타입 int_gmp가 다형성 변형 타입(polymorphic variant type)과 호환되지 않기 때문입니다.
경고: 현재 모듈의 추상 타입 (Warning: Abstract types in the current module)
눈치 빠른 독자는 위 정의에서 다음을 알아차렸을 것입니다,
OCaml 5.4.0에서는 타입체커가 다음 코드를 total로 받아들입니다.
type int_gmp
let ok: (int_gmp, [` A] ) Type.eq -> _ = function _ -> .
실제로 OCaml 5.5.0까지는 현재 모듈에서 정의된 추상 타입(abstract types)은
type a
type b
고유하며 증명 가능하게 다르다고 간주되었습니다.
let f: 'x. (a,b) Type.eq -> 'x = function _ -> .
하지만 로컬 정의된 추상 타입에 대한 이 특별한 규칙은 매우 취약했습니다. 현재 모듈을 벗어나면 더 이상 해당 타입들이 다르다는 것을 증명할 수 없게 되었습니다.
module M = struct
type a
type b
...
Error: This match case could not be refuted.
Here is an example of a value that would reach it: Equal
이 특별한 타입체킹 규칙은 OCaml 5.5.0에서 제거되었습니다. 만약 이 기능에 의존하고 있었다면, 예를 들어 GADTs에서 추상 타입을 타입 레벨 레이블로 사용했다면, 추상 타입 정의를 다음의 다형 변종(polymorphic variant)의 가능한 private 축약어
type a = private [`A]
type b = [`B]
또는 (가능한) private 합집합 타입(sum type)
type a = A
type b = private B
으로 변경할 수 있습니다.
추상 타입을 타입 레벨 레이블과 FFI 타입 모두로 사용했다면, 이제 외부 타입 정의를 사용할 수 있으며, 이는 현재 모듈 밖에서도 증명 가능하게 구별되는 타입을 제공합니다.
GC 개선 사항
가비지 컬렉터(garbage collector)의 속도 향상을 위한 지속적인 작업 중 일부가 OCaml 5.5.0에 통합되었으며, OCaml 5.5 GC에서 중요한 두 가지 변경 사항은 다음과 같습니다.
- 주요 GC 시작 시 스윕 전용 단계(sweep-only phase) 추가
- 시작 시 GC 동작을 부드럽게 하기 위한 유휴 단계(idle phase) 추가
다수의 점진적 변경 사항
- Windows 구현이 Winpthreads에 의존하지 않음
- 약 60개의 새로운 표준 라이브러리 함수
- 약 90가지의 다양한 개선 사항
- 수십 개의 문서 업데이트
- 약 40개의 버그 수정
예상치 못한 동작은 OCaml 이슈 트래커에 보고하고, 질문이나 의견은 토론 포럼에 게시해 주십시오.
전체 변경 목록은 전체 changelog에서 확인할 수 있습니다.
즐거운 해킹 되세요,
Florian Angeletti 드림 (OCaml 팀)
설치 방법
기본 컴파일러는 다음 명령어를 사용하여 opam 스위치로 설치할 수 있습니다:
opam update
opam switch create 5.5.0
릴리스의 소스 코드는 다음에서도 직접 이용 가능합니다:
미세 조정된 컴파일러 구성
컴파일러 구성을 변경하고 싶다면, 다음 명령어로 옵션 변형(option variant) 스위치로 전환할 수 있습니다:
opam update
opam switch create <switch_name> ocaml-variants.5.5.0+options <option_list>
여기서 <option_list>는 ocaml-option-* 패키지의 공백으로 구분된 목록입니다. 예를 들어, flambda와 no-flat-float-array 스위치의 경우:
opam switch create 5.5.0+flambda+nffa ocaml-variants.5.5.0+options ocaml-option-flambda ocaml-option-no-flat-float-array
OCaml 5.5.0의 변경 사항
언어 기능:
- #14040:
let module,let exception, 및let open구문을 대부분의 다른 구조 항목에 일반화합니다. 예를 들면:
let type t = ... in ...
let type Effect.t += Yield in ...
(Nicolás Ojeda Bär, Valentin Gatien-Baron 검토)
- #13806: 함수 매개변수에 다형적 타입 사용을 활성화합니다.
let extract (getter : 'a . 'a t -> 'a) = ...
let runST (m : 's . ('s, 'a) ST.t) : 'a = ...
(Ulysse Gérard, Leo White, Florian Angeletti, Samuel Vivien, Gabriel Scherer 및 Jacques Garrigue 검토)
- (호환성 파괴 변경) #13712: 외부 타입(external type)을 식별하기 위한 새로운 키워드
Type_external과 구문 `type t = external
해당 구조체 항목의 AST 노드에 연결되며 (전역적 대응물과 유사하게), 더 이상 포함하는 let 표현식에 연결되지 않습니다. 포함하는 let 표현식에 연결되어야 하는 확장 지점/속성은 let 키워드 옆에 작성해야 합니다. 예: let%foo[@bar] module ... in .... 이는 let exception과 let open에도 동일하게 적용됩니다.
(Nicolás Ojeda Bär, 검토: Gabriel Scherer)
- #14029:
%identity를 비확장성(nonexpansive)으로 인식하도록 변경
(Stephen Dolan and Olivier Nicole, 검토: Hugo Heuzard, Jacques Garrigue, Jeremy Yallop, 그리고 Gabriel Scherer)
표준 라이브러리:
- #13372: 가변적인 리스트를 인수로 받는
Printf및Format.lprintf와 같은 함수 형식 추가.
Format.lprintf "@[%s@ %d@]@." [ "x ="; 1 ]
(Leonardo Santos, 검토: Florian Angeletti and Gabriel Scherer)
- #14437:
String.split_{first,last,all}및String.rsplit_all추가
split_first: sep:string -> string -> (string * string) option
split_all: sep:string -> drop:(string -> bool) -> string -> string list
(Daniel Bünzli, 검토: Nicolás Ojeda Bär and Florian Angeletti)
- #14436:
String.replace_{first,last,all}추가
replace_first: sub:string -> by:string -> ?start:int -> string -> string
(Daniel Bünzli, 검토: Nicolás Ojeda Bär, Ali Caglayan and Florian Angeletti)
- #14439: 세트 완성화를 위해 String.includes 추가:
starts_with: prefix:string -> string -> bool
ends_with: suffix:string -> string -> bool
includes: affix:string -> string -> bool
(Daniel Bünzli, 검토: Nicolás Ojeda Bär and Olivier Nicole)
- #14381:
String.find_{first,last}_index,String.find_{first,last},String.[r]find_all추가.
find_first_index: (char -> bool) -> ?start:int -> string -> int option
(Daniel Bünzli, 검토: Nicolás Ojeda Bär, Ali Caglayan and Florian Angeletti)
- #14438:
String.is_empty추가
(Daniel Bünzli, 검토: Nicolás Ojeda Bär and Gabriel Scherer) - #14440:
String.of_char추가
(Daniel Bünzli가 작성하고 Nicolás Ojeda Bär와 Gabriel Scherer가 검토함)
#14352: String에 {drop, take, cut}_{first, last} 추가
take_first: int -> string -> string
cut_first: int -> string -> string * string
(Daniel Bünzli가 작성하고 David Allsopp, Nicolás Ojeda Bär와 Vincent Laviron이 검토함)
#14362: String에 {drop, take, cut}_{first, last}_while 추가
drop_first_while: (char -> bool) -> string -> string
(Daniel Bünzli가 작성하고 Nicolás Ojeda Bär와 David Allsopp이 검토함)
#13916: Option.product 및 Option.Syntax 추가
(Nicolás Ojeda Bär가 작성하고 Daniel Bünzli, Gabriel Scherer, David Allsopp이 검토함)
#13995: Option.blend: ('a -> 'a -> 'a) -> 'a option -> 'a option -> 'a option
(Kate Deplaix가 작성하고 Daniel Bünzli, Gabriel Scherer, Nicolás Ojeda Bär, Florian Angeletti와 Josh Berdine이 검토함)
#13920: Option에 {for_all, exists} 추가
(Gabriel Scherer가 작성하고 Kate Deplaix, Nicolás Ojeda Bär, Richard Eisenberg와 Jeremy Yallop이 검토함)
#14185: List.split_map: ('a -> 'b * 'c) -> 'a list -> 'b list * 'c list
(Jeremy Yallop가 작성하고 Daniel Bünzli, Nicolás Ojeda Bär와 Damien Doligez가 검토함)
#14043, #14393: Lazy.Mutexed: 초기화 경쟁으로 인해 전체 도메인/스레드를 차단할 수 있는 간단한 뮤텍스로 보호된 lazy thunks.
(Gabriel Scherer가 작성하고 Kate Deplaix와 Pierre Chambart가 제안했으며, KC Sivaramakrishnan, Florian Angeletti, Kate Deplaix와 Daniel Bünzli가 검토함)
#14118: {Set,Map}.S.is_singleton 추가
(Kate Deplaix가 작성하고 Daniel Bünzli, Vincent Laviron, Nicolás Ojeda Bär와 Stephen Dolan이 검토함)
#14060: Hashtbl.find_and_replace 및 Hashtbl.find_and_remove 추가
find_and_replace: ('k, 'a) Hashtbl.t -> 'k -> 'a -> 'a option
find_and_remove: ('k, 'a) Hashtbl.t -> 'k -> 'a option
(Sacha-Élie Ayoun가 작성하고 Nicolás Ojeda Bär와 Gabriel Scherer가 검토함)
#14227: List.filter_mapi 추가
(Émile Trotignon가 작성하고 Nicolás Ojeda Bär, Jan Midtgaard와 Damien Doligez가 검토함)
더 많은 표준 라이브러리 변경 사항
#14432: Int, Int32, Int64, 그리고 Nativeint 모듈에 바닥 나눗셈(floor division), 올림 나눗셈(ceil division), 유클리드 나눗셈(Euclidean division) 및 나머지 연산자 추가
(fdiv, cdiv, ediv)
(Xavier Leroy, Ali Caglayan, Nicolás Ojeda Bär, Gabriel Scherer가 검토함)
#14433: Int, Int32, Int64, 그리고 Nativeint에 비트 카운팅 함수(leading_zeros, leading_sign_bits, trailing_zeros, bit_count, unsigned_bitsize, signed_bitsize) 추가
(Xavier Leroy, Ali Caglayan 및 David Allsopp가 검토함)
더 많은 표준 라이브러리 변경 사항
#10177: Seq.(delay : (unit -> 'a t) -> 'a t)
(Gabriel Scherer, Jeremy Yallop 및 François Pottier가 검토함)
#13343: Array.stable_sort_sub 추가
AI 자동 생성 콘텐츠
본 콘텐츠는 Lobste.rs ML의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기