1P by GN⁺ 7시간전 | ★ favorite | 댓글 1개
  • OCaml을 주요 프로그래밍 언어로 선택한 경험 중심의 내용임
  • 이 언어의 정적 타입 시스템함수형 프로그래밍 지원이 큰 장점으로 언급됨
  • 다양한 도메인에서 생산성과 코드의 안정성이 높아진 경험을 강조함
  • 생태계와 도구에서 개선이 이루어졌으며, 타 언어 대비 더 매끄럽게 작업할 수 있음
  • OCaml 사용을 더 많은 개발자가 고려할 만한 충분한 이유와 배경을 제공함

OCaml을 주요 언어로 선택한 이유

  • 필자는 오랜 기간 다양한 프로그래밍 언어를 사용했으며, 그 중에서도 OCaml을 주요 언어로 선택한 경험을 바탕으로 글을 구성함
  • OCaml의 가장 큰 장점으로는 강력한 정적 타입 시스템과, C나 다른 함수형 언어에 비해 뛰어난 함수형 프로그래밍 지원을 꼽음
  • Said 타입 시스템 덕분에 많은 버그 예방과 코드 최적화 경험이 있었음
  • 실제로 여러 개발 프로젝트에서 OCaml을 활용해 생산성안정성이 대폭 향상된 경험을 했음

OCaml의 장점과 실무 활용

  • 대부분의 코드가 빠르게 작성되며, 함수 조합불변 데이터 사용으로 안전성이 높아짐
  • 최근에는 OCaml의 생태계도구(IDE, 빌드 시스템 등) 도 지속적으로 발전하는 추세임
  • 다양한 라이브러리와 외부 패키지 덕분에 실무에서 효율적인 개발이 가능해짐
  • Python, Java와 비교했을 때, OCaml은 덜 유명하지만 생산성, 안전성, 유연성 부분에서 매우 강력한 선택지임

새로운 도전과 커뮤니티

  • OCaml 커뮤니티는 작지만 지속적으로 성장하고 있으며, 사용자 친화적인 흐름을 보임
  • 새로운 언어나 패러다임에 대한 도전을 원하는 개발자에게 OCaml은 깊이 있게 배울 만한 가치가 있음
  • 많은 사용자는 OCaml의 사용 경험을 통해 새로운 시각문제 해결력이 높아진다고 언급함

결론

  • OCaml은 특정 영역(예: 금융, 컴파일러, 시스템 개발)에 국한되지 않고 범용적으로 활용할 수 있는 강력한 프로그래밍 언어
  • 실전에서 얻은 효율성, 유지보수성, 문제 방지 능력 등은 실제 업무 현장에서 그 가치를 증명함
  • 최신 언어나 트렌드에 비해 다소 덜 알려져 있다 해도, 신뢰도와 안전성을 중시한다면 충분히 고려할 만한 선택지
Hacker News 의견
  • 나는 Google에서 Rust를 Android 팀에 도입한 경험에 대한 발표를 본 적이 있음. 그 중 두 가지가 인상적이었음: 다양한 프로젝트를 Python에서 Rust로 옮겼으니 성능이 그렇게 큰 이슈는 아니었을 것 같고, Rust 사용자들이 가장 좋아한 기능은 패턴 매칭과 ADT(Algebraic Data Types)같은 기본적인 것들이었음. 그래서 Rust가 진짜로 크게 기여한 부분은 수명(Lifetime) 같은 고유 기능보다는 1990년대 ML 언어가 이미 제공하던 요소라고 느낌. 만약 OCaml이 2010년쯤 멀티코어 같은 불편함들을 해결했다면 Rust만큼 인기도 있었을 것 같았음. 아쉽게도 OCaml은 학계와 산업계 사이 간극에 빠짐. 한 가지 덧붙이자면 31비트 정수는 비트 연산할 때 실용적으로 불편하고, 미관상으로는 이중 세미콜론이 정말 마음에 안 들었음

    • OCaml은 그 당시 이미 상당히 괜찮은 상태였다고 생각함. 2010년에 직업적으로 Python보다 훨씬 쾌적하게 사용함. JaneStreet가 무엇을 이룩했는지만 봐도 됨. OCaml이 널리 채택되지 못한 가장 큰 원인은 미국에서 만들어지거나 주도되지 않았기 때문이라고 봄. 언어의 인기가 기술적 우수성 때문이라고 믿고 싶겠지만, 결국 유행의 문제임. Rust가 대중적으로 성공한 원인도 대량의 홍보와 적극적인 커뮤니티 활동 덕분임. 전담 직원까지 두고 적극적으로 이름을 알렸음

    • Google은 실제 서비스 코드에 쓸 수 있는 공식 언어 리스트를 가능한 짧게 유지하려고 노력함. Rust가 C++을 대체·보완할 수 있는 언어라서 선택된 것 같음. OCaml은 그런 위치에 놓이기 어려웠음(Go는 대체할 수 있었겠지만 가능성 낮음). 그래서 Rust가 선택된 가장 큰 이유는 ADT를 제공하는 공식 언어 중 유일했기 때문이지, 빌드 속도를 중시하지 않아서가 아닐 것임. OCaml이 Rust를 대체하지 못한 것도 당연함. GC가 있는 언어는 이미 Go, Haskell 등 다양했고, 2010년 즈음에 베어메탈을 노릴 만한 표현력이 뛰어난 언어는 C++뿐이었음(그마저도 C++11·C++17 이전엔 더 별로였음)

    • 완전 동의함. OCaml이 몇몇 자잘한 문제를 해결했으면 진짜로 중요한 플레이어가 될 수 있었음. 빌드 속도는 지금도 Rust보다 훨씬 빠름. 그런데 OPAM(패키지 관리자)은 자주 버그가 생기고, 헷갈리기로 유명함. Windows 지원은 아주 심각할 정도로 나쁨. 과거 Perl의 Windows 지원보다도 더함. 공식 문서는 너무 간결해서 쓸모가 없을 정도임. 문법 자체도 파악하기 어렵고, 사소한 오타로 인해 파일 절반이 문법 오류라는 식의 메시지도 자주 뜸. Rust의 기존 C 스타일 문법이 훨씬 수월함. 정리하면, OCaml의 이점은 빠른 빌드뿐인데, 그것만으론 굳이 쓸 이유가 부족함

    • 그래서 내가 ML 스타일로 프로그래밍하고 싶을 땐 러스트보단 Kotlin, Scala, F#을 먼저 찾음. 그리고 요즘 Java나 C#조차도 이미 충분히 많은 ML 요소를 채택해서 큰 거부감 없음. Caml Light나 Objective Caml 시절부터 ML 타입 시스템에 익숙한데, 요즘 Rust에 사람들이 열광하는 걸 보면 마치 Rust가 ML 타입 시스템을 새로 들고 온 것처럼 착각하는 것 같음

    • OCaml이 좀 더 잘 준비했으면 좋았겠다는 의견에 대해, 실제로는 언어 선택의 옵션이 다양하다는 점이 가장 큰 장점이라고 생각함. 영국만 해도 (인구는 적어도) 엄청 다양한 언어가 공존함. 예를 들어 유럽의 죽은 언어인 코니시도 최근 지역민들에 의해 부활했고, 목동들 사이에서는 쿠브릭이라는 숫자 세는 언어도 남아 있음. 나도 다음 세대의 가계도를 위해 OCAML 기반의 Geneweb이라는 프로그램을 쓰기 시작했음(TMG라는 Windows 앱에서 이전). 가족 데이터가 14만 명이나 담겨 있음. Geneweb이 OCAML로 만들어져 있어서 언어에 대한 관심이 커짐. 만약 프로그래밍 언어가 어렵다고 느낀다면, 족보/계보학을 한번 해보길 추천함. 곧 GEDCOM이라는 규격 때문에 머리가 아파질 것임

  • 나는 OCaml이 내 사랑하는 언어 중 하나임. 가장 많은 작업은 Writer's Festival 조직을 위해 CRUD 앱을 OCaml(ReasonML 기반 JSX), Dream, HTMX, DataTables로 100% 구현했음. 모듈로 프론트엔드 템플릿을 재사용했고, 데이터 모델에 변경사항이 생기면 컴파일러가 어디서 깨졌는지 바로 보여줘서 매우 만족했음. 엑셀 데이터도 제대로 된 DB로 이동시키고, .odt 형식의 템플릿 일정표나 서버 디스크를 거치지 않고 바로 zip 파일로 내보내는 등 OCaml 생태계에서 놀랍게 많은 것을 구현했음. 다만, DB 쿼리를 다 문자열로 써야 하고, 타입 변환도 수작업이라서 피로도가 하늘을 찔렀음(컴파일 타임 타입 체크가 안 됨). 인증 시스템도 직접 구현해야 해서, 핵심 제품 개발이 아닌 일에 너무 많은 시간을 쏟기 일쑤였음. 여러 언어를 둘러본 다음 느낀 점은 완벽한 언어란 없다는 것임. 모든 언어가 각자만의 독특한 단점이 있음. 지금은 자신만을 위한 앱을 Rails로 만들고 있는데, 필요한 것들은 거의 다 기본값으로 제공되어서, 언어보단 실제 레이아웃 디자인이나 실제 배포 등 본연의 작업에 집중할 수 있어서 훨씬 만족스러움

    • 강한 타입의 함수형 언어에서는 DB 결과를 어떻게 처리하는 것이 관용적인지 궁금함
  • DarkLang은 처음에는 OCaml로 개발되다가, 나중에 F#으로 전환했음. 그 주된 이유는 라이브러리 생태계와 동시성 때문임(관련 글). 나는 .NET에 익숙해서 어느 정도 편향이 있을 수 있지만, 지루한 부분들도 여러 선택지가 잘 마련되어 있어 본질적인 문제에 집중할 수 있음. F#을 직업적으로 쓴 경험이 꽤 있고, 인기 있는 UI 라이브러리도 유지하고 있지만, 언어 생태계가 작아서 닷넷에서도 솔루션이 항상 바로 되는 건 아님. 그래서 주류에서 벗어난 언어를 고르면(예: C# 대신 F#) 비용이 든다는 점을 염두에 둬야 함. OCaml도 마찬가지로, 강력한 언어를 제공하지만, 주류에서 벗어났기 때문에 여러 불편함이 있음. 몇몇 회사들이 실서비스에서 쓰고는 있지만, 그들만의 독특한 요구에 맞춘 사례임

  • OCaml을 몇 년간 좋아해보려고 노력해봤는데, 가장 불편했던 부분은 '임의의 객체를 print 할 수 없는 것'이었음. ppx로 to_string 함수를 자동으로 파생시킬 수 있지만, 설정이 귀찮고 Rust에 비해 사용성이 떨어짐. Set, Map 등의 타입을 출력하려면 추가 작업이 필요함(참고 사례). golang에서는 "%v" 포매팅으로 거의 모든 걸 쉽게 출력할 수 있는데, OCaml은 그런 점에서 손이 더 감

    • Go의 %v 포매팅도 완변하지 않고, 더 깊이 포인터를 트래버스하려면 go-spew 같은 라이브러리가 별도로 필요함. 파이썬의 repr 방식이 지금까지 봤던 것 중 가장 편함
  • OCaml을 직접 써보진 않았지만 F#으로 작업한 경험이 매우 쾌적했음. 요즘 LLM 시대에는 함수형 언어를 다시 주목해보면 좋겠다는 생각임. OCaml, Haskell 같이 함수형 패러다임에서는 정보를 작은 텍스트에 효율적으로 압축할 수 있는 만큼, LLM의 컨텍스트 윈도에도 더 많은 의미를 담을 수 있지 않을까 싶음. Java, C#, Ruby에 비해 더 복잡한 변화도 한 번에 적용할 수 있을지 실험해볼 만함

    • 나도 처음엔 그럴 줄 알았는데, 실제로 대형 Haskell 코드베이스에서 일하면서 생각이 바뀜. 훈련 데이터셋에 FP가 부족해서 그런지, 더 간결한 언어가 오히려 LLM엔 잘 안 맞는 것 같음. 코드가 verbose해야 LLM이 잘못된 토큰 예측 후 자신을 바로잡을 기회가 많아 더 올바른 코드를 생성하는 느낌임

    • 내 개인적인 실험으로는 C++와 Haskell로 간단한 CLI 게임을 만들어봤는데, 줄 수는 Haskell이 적었으나 단어 수는 거의 비슷해서 코드가 '넓어보일' 뿐임. Java나 좀 더 명시적인 언어와 비교해보진 않았지만, 프로그램 성격에 따라 적합한 스타일이 달라진다고 생각함. 어떤 것은 명령형 스타일이, 어떤 것은 함수형이 더 적합할 수도 있음

    • LLM이 코드 생성 실력이 조금만 더 발전한다면, 정말 강력한 타입 시스템과 효과 시스템으로 코드의 동작 범위에 제약을 둘 수 있으면 좋겠음. 예를 들어 종속 타입(dependent types)이 있다면 "이 함수는 반드시 정렬된 리스트를 반환함"이나 "이 함수는 반드시 유효한 스도쿠 솔루션을 반환함" 같은 조건을 컴파일 타임에 검사할 수 있음. 여기에 효과 시스템을 붙이면 "이 함수는 유효한 스도쿠 솔루션을 반환하지만 네트워크나 파일시스템은 접근하지 않음"으로 지정 가능해짐. LLM이 더 진화하면 이런 정도도 Python에서 해낼 수 있겠지만, 발전 속도가 더딜 경우엔, 신뢰성 부족한 LLM을 신뢰성 높은 결정적 시스템으로 감싸 활용하는 쪽이 미래라고 봄

    • Scala에서 cats-effect(이펙트 라이브러리)를 쓸 때 LLM의 도움으로 개발 속도가 엄청 빨라졌음. cats-effect 코드는 쉬운 개념도 어렵게 느껴질 때가 많은데, LLM에 "cats-effect에서 ~하려면 어떻게 해?"라고 묻기만 하면 80%는 바로 해결됨. 나머지 20%는 추가 컨텍스트를 주면 됨. 유지보수 관점에서는 아직 시험 중이지만, 효과 기반 함수형 프로그래밍의 좌절감이 크게 줄었음. 다음엔 Claude Code가 얼마나 잘하는지 실험해보고 싶음

    • Haskell은 LLM 코드 생성에서 두 가지 큰 장점이 있음. 첫째, 표현력 높은 타입 시스템이 많인 실수를 잡아서 발생한 컴파일 에러 자체를 다시 LLM에게 피드백 줄 수 있음. 둘째, 프로퍼티 기반 테스트(QuickCheck 등)을 통한 효율적이고 정확한 코드 개선이 쉬움. LLM이 테스트 자체를 잘 작성하지는 못하지만, 직접 추가해주면 생성된 코드의 버그도 빨리 잡아냄

  • 이 글을 보고 "왜 F# 대신 OCaml을 안 써?"라는 질문에 종지부를 찍게 됨. 거의 모든 OCaml 관련 스레드에서 "F# 쓰면 도구 문제 해결 아냐?"라는 제안이 나옴. 나도 OCaml이 궁금했고 "Go with types"란 별명을 봐서 관심이 있었지만, 아직 OCaml 자체가 완전히 매력적으로 다가오지는 않음. Erlang, Ruby, Rust, Zig 등 다른 언어 커뮤니티의 열정과는 뭔가 다름

    • 나는 오히려 F# 도구 생태계를 피하려고 OCaml로 넘어온 사람임. F#의 경우 내가 썼을 땐 컴파일러가 느리고, C#에 집중되는 생태계, 약하고 문서도 부족한 MSBuild, 자꾸 크래시 나는 Ionide, 비신뢰적인 Fantomas 등 도구적 문제가 많았음. 다만 OCaml도 F#의 성능 지향 기능(예: 값 타입 등 CLR이 지원하는 부분)을 다 대체하지는 못함. 그런 점에서 아직까지는 간단한 ML 계열 언어를 찾지 못했음. 향후 OxCaml 등이 해결할 수 있기를 기대 중임

    • 최근에는 OCaml을 잘 쓰지 않았지만, 언어의 핵심 자체는 여전히 가장 선호함. 내 코드 스타일이 커다란 하나의 함수에 쏠리는 경향이 있는데, OCaml에서는 자연스럽게 그걸 피하게 됨. 러스트를 사이드 프로젝트에서는 쓰고 있지만, 사실 OCaml이 더 편함. 위 이유 때문에 F#도 꼭 한번 써보고 싶음

  • 용어에 대해 질문이 있음: 기사에서 함수형 타입을 "지수 타입(exponential types)"이라고 부르는데, 고차 함수 타입을 왜 지수 타입이라고 하는지 잘 이해가 안 됨

    • 이미 좋은 설명이 있지만, 더 깊은 이유는 함수형 타입이 대수적으로 지수법칙을 따른다는 것임. 예를 들어 A → (B → C)는 커링을 통해 (A × B) → C와 동형임. 이는 (cᵇ)ᵃ = cᵇ˙ᵃ와 유사함. 그리고 (A + B) → C는 (A → C) × (B → C)와 동형인데, 이는 cᵃ⁺ᵇ = cᵃ·cᵇ 규칙에 해당함

    • 1차 함수형 타입도 이미 지수적임. 예를 들어 sum 타입은 케이스의 개수만큼 값이 존재함. (예: A of bool | B of bool → 2+2=4가지). 곱 타입과 지수 타입도 마찬가지임. bool -> bool로 표현하면 2^2 = 4개의 가능한 값이 있음(부작용을 생각하지 않으면)

    • 보통 ADT(Algebraic Data Type) 얘기할 때 sum과 product만 다룸. 함수는 데이터가 아니라서 잘 언급되지 않음. 하지만 a -> b 타입은 b^a 개의 경우가 있으므로 동일한 방식으로 접근할 수 있음

    • 나도 같은 의문이 있었는데, 수학적으로 합(sum), 곱(product) 다음엔 지수(exponent)가 오니까, 비유적으로 그렇게 부르는 듯함

    • 답글도 모두 맞는 얘기지만, 실은 범주론(category theory)에서 함수형 타입을 "exponential product"라고 부름. 그 이름도 A에서 B로의 함수 개수가 B의 기수^A의 기수로 계산되는 것에서 유래함

  • Sum types: 예를 들어 Kotlin, Java, 그리고 C#은 sealing(상속 관계 봉인)을 써서 특정 케이스를 자신의 타입처럼 다룰 수 있음
    sum 타입 선언이 장황해서 이해하기 어렵다는 주장
    sum 타입은 보통 한 번만 선언하고 여러 번 사용하게 됨. 케이스가 더 깔끔하게 사용된다면, 선언 시 약간의 장황함 정도는 충분히 감수할 만함

    • sum-type의 케이스는 타입 생성자(type constructor)를 통한 값(expression)이기 때문에, 당연히 타입을 가짐. 예를 들어,

      datatype shape =
         Circle of real
        | Rectangle of real * real
        | Point
      

      각각의 케이스는 타입이 부여됨. 패턴 매칭 덕분에 이미 타입 생성자의 파라미터를 바로 언패킹함. 케이스를 별도 타입으로 뽑아내면 sum-type이 가진 exhaustiveness(누락 방지) 이점이 사라져 오히려 잘못된 프로그램 상태를 표현하게 됨. sum-type은 선언 한 번만 하고 여러 번 쓸 수 있고, 보통 disposable함. 코드 읽기도 중요하니 장황함이 과소평가되는 부분이 있음. 참고로 C#/Java는 진짜 sum-type을 지원하지 않음. 아래 예시를 보면 C#이 OOP적인 방식 때문에 불필요하게 복잡함

      public abstract record Shape;
      public sealed record Circle(double Radius) : Shape;
      ...
      double Area(Shape shape) => shape switch {...};
      

      ML에서는 훨씬 더 간결하게 처리

      datatype shape = Circle of real | Rectangle of ... | Point
      val result = case shape of ...
      

      두 방식이 거의 동일하나, C#의 OOP 요소가 오히려 걸림돌임

    • OCaml에서는 GADT, 다형 변형(Polymorphic Variants) 등을 써서 각각 별도 타입처럼 쓸 수도 있음. 하지만 대체로 sum-type을 분리하면 일반화도 어렵고 이해도 힘들어짐. 타입 평등성과 variance 문제도 동반됨

    • 굳이 sum-type과 sealed-type 논쟁을 왜 하는지 모르겠음. 함수형 언어를 더 선호하지만, 타입 레벨에서의 차별성 덕분에 sealed 타입만으로도 sum-type을 다 모델링 가능하고, 서브타이핑 덕분에 정의와 사용이 더 쉬운 측면도 있음. 시스템의 패러다임은 많이 다르지만, 수학적으로는 거의 동등하고, OOP·FP에서 가능한 타입 장난은 언어가 허용하는 범위에서 거의 다 구현 가능함

    • Java/Kotlin의 sum-type 선언이 장황함을 감수할 만한 가치가 있다는 데 동의하지 않음. JVM 언어의 전형적 보일러플레이트라는 인상임

  • ReasonML 문법을 이만큼 잘 아는 사람이 장단점을 비교해주면 좋겠음. (기사에서 짧게 언급됐지만)

    • 내가 가장 아쉬웠던 건 let 바인딩(공식 문서)임. ReasonML에서는 모나드를 위한 >>= 같은 연산자를 직접 커스텀해서 다루기 쉬웠음. rescript(ReasonML의 포크)는 그게 아직도 없음. 대신 async/await 문법은 잘 지원해서 비동기 코드엔 도움이 됨. Melange(기사에서 잠깐 언급됨)는 Reason 문법에서 let 바인딩을 지원함. 그래서 React 기반 프론트엔드에선 Melange의 Reason ML이 굉장히 유리함. let 바인딩 덕분에 (JSX와 함께) 모나딕 스타일 비동기 코드도 깔끔히 쓸 수 있음. OCaml 문법에선 PPX로 우회할 수는 있는데, 편집기에서 하이라이트가 잘 안됨. 백엔드로 보면, 나는 Python 스타일을 좋아해서 중괄호가 아직도 거슬리고, 함수 호출·정의시 괄호 없이 쓰는 걸 좋아함. 하지만 초보 OCaml 유저로서 비변수 전달 인자 사용시 괄호가 헷갈려서 여전히 어렵게 느껴짐. 이 경험이 도움이 됐길 바램

    • ReasonML 별로 안 써봐서, 장점을 못 느꼈음. 4년 동안 두 번이나 죽어버렸다는 점 외엔...

    • Reason 문법이 더 널리 보급됐으면 좋겠지만, OCaml 커뮤니티와 소통하려면 표준 문법을 그냥 익히는 게 나음. 대부분의 코드나 문서는 표준 문법이라서 결국 알아둬야 함

    • 내가 경험한 ReasonML에서 가장 불편했던 건 LSP가 제대로 동작하지 않았음

  • dependency injection을 effects system으로 구현하는 부분을 더 자세히 설명해줬으면 좋겠음. 패턴 매칭으로 테스트 값/프로덕션 값을 바인딩한다는 아이디어가 재미있어 보이지만, 글만으론 감이 안 옴. 그리고 module system에 자체 타입 시스템이 있다는 것도 처음 알게 되어 신기함

    • 난 Haskeller임!

      예제에서 패턴 매칭 통해 테스트 값/프로덕션 값 바인딩에 대해 감이 안 온다는 질문에 대해... 기사에서 말하고 있는 건 free monad + interpreter 패턴으로, 비즈니스 로직에서 직접 액션을 수행하지 않고, 작업을 명사로 만들어 AST로 누적함. AST가 생기면 ProdAstVisitor, TestAstVisitor로 실제 실행·테스트를 분리할 수 있음. 구체적으로, AST의 각 노드에서 Test.ReadFile/WriteFile을 선택한다는 얘기고, Test와 Prod가 직접 혼합되는 건 아님. Haskell 커뮤니티는 tagless final 접근 방식이 ceremony도 덜고 효과가 같다는 이유로 free monad + interpreter 패턴에서 조금 멀어진 추세임. effects system으로 DI 구현을 더 보고 싶다는 의견에 대해... 난 effects로 DI를 하고 있음. 내가 만족하는 방식은 이러함: 제일 하위에는 readTextFile, writeTextFile 등 기능별로 분리된 capability 인터페이스를 둠. 이 클래스들은 업무(domain) 지식을 전혀 몰라야 함. 그 위에서는 업무와 capability를 조합해 비즈니스 함수(예: fetchStoredCommands)를 구현함. 제일 상위에서는 CliApp이라는 타입이 있고, 이곳에서 capability 인터페이스의 실제 구현체와 바인딩함. 예를 들어, Logger를 따로 Mock 버전으로 연결하면, 프로덕션/테스트 환경의 환경 분리를 타입클래스로 구현함. 중요한 점은, 전체 함수가 효과 타입(IO 여부 등)을 타입 시스템상에 모두 명시함. 실제 프로덕션용 구현은 IO가 허용되지만, 테스트 구현에서는 IO가 불가함. 만약 새 capability(Caching 등)를 만들고 테스트 구현체를 누락하면 컴파일 타임 오류로 잡혀서, 강한 안정성이 생김. OCaml에서도 이와 비슷한 방식을 쓸 수 있을지 궁금함. 해당 기사에서는 아직 effect 전파가 타입 시스템에 의해 추적되지 않는다고 했음