4P by GN⁺ | ★ favorite | 댓글 2개
  • Railway Oriented Programming은 검증·로깅·네트워크 오류처럼 현실 애플리케이션에서 반복되는 실패 경로를 함수형 파이프라인 안에서 다루는 접근임
  • 성공 경로만 이어 붙이는 예제의 한계를 레일웨이 비유로 풀어, F# 사용자도 오류 흐름을 시각적으로 이해하기 쉽게 만든 것이 핵심임
  • Haskell 관점에서는 Either 타입과 Kleisli 합성에 가깝지만, 추상 이론보다 오류 처리 레시피와 구현 패턴에 초점을 맞춤
  • 실제 적용을 위해 NuGet용 F# 라이브러리 Chessie, C#·F# 비교 GitHub 예제, FizzBuzz 적용 예제가 함께 제공됨
  • 유용한 방식이지만 모든 문제에 과도하게 적용하면 안 되며, F#에는 type class가 없어 예제 라이브러리는 필요한 함수를 직접 정의함

발표 자료와 예제 코드

  • Railway Oriented Programming 페이지는 발표 슬라이드와 코드를 한곳에 모은 자료임
  • 함수형 프로그래밍 예제가 자주 가정하는 “happy path”만으로는 현실 애플리케이션을 만들기 어렵다는 문제에서 출발함
  • 견고한 애플리케이션은 검증, 로깅, 네트워크 오류, 서비스 오류, 기타 예외적 상황을 함께 처리해야 함

영상과 슬라이드

  • 이 주제는 NDC London 2014에서 발표됨
  • 같은 발표의 다른 영상도 공개되어 있음
  • Functional Programming eXchange의 2014년 3월 14일 발표 슬라이드가 있음
  • PowerPoint 슬라이드는 GitHub에서도 받을 수 있으며, 자유롭게 빌려 써도 된다고 안내함

Either 모나드와 Kleisli 합성

  • Haskell 사용자에게 이 접근은 Either 타입으로 보일 수 있음
  • 여기서는 Left 케이스에 커스텀 오류 타입 목록을 쓰는 형태로 특화됨
    • 예시 타입은 type TwoTrack a b = Either [a] (b,[a])
  • 새로 만든 것은 오류 처리 개념 자체가 아니라, 이를 설명하는 레일웨이 비유에 가까움
  • 표준 Haskell 용어를 앞세우지 않은 이유는 명확함
    • 이 자료는 모나드 튜토리얼이 아니라 오류 처리 문제 해결에 집중함
    • F#을 접하는 많은 사람은 모나드에 익숙하지 않아, 시각적이고 덜 위협적인 설명이 더 직관적일 수 있음
    • bind가 있는 two-track 타입을 곧바로 모나드라고 부르는 것은 부정확하며, 모나드 법칙까지 다루지 않음
    • Either는 너무 일반적인 도구라서, 여기서는 도구 자체보다 레시피를 제공하려 함

오류 처리 레시피의 구성 요소

  • 이 접근은 “그냥 Eitherbind를 쓰면 된다”보다 더 포괄적인 기법 묶음임
  • 포함되는 기법은 다음과 같음
    • Either String a 같은 단순 형태 대신, 왼쪽과 오른쪽 모두에서 커스텀 오류 타입 목록 사용
    • bind(>>=)로 모나딕 함수를 파이프라인에 통합
    • Kleisli 합성(>=>)으로 모나딕 함수 합성
    • map(fmap)으로 비모나딕 함수를 파이프라인에 통합
    • F#은 IO 모나드를 쓰지 않으므로, tee로 unit 함수를 파이프라인에 통합
    • 예외를 오류 케이스로 매핑
    • &&&로 검증 같은 상황에서 모나딕 함수를 병렬 결합
    • 도메인 주도 설계에서 커스텀 오류 타입이 주는 이점 활용
    • 로깅, 도메인 이벤트, 보상 트랜잭션 같은 확장
  • 목표는 거의 모든 상황에서 쓸 수 있을 만큼 다용도이면서도, 일관된 스타일을 강제할 만큼 제한적인 템플릿을 만드는 것임
  • 코드 작성 방식이 사실상 하나로 수렴하면, 나중에 유지보수하는 사람이 코드 구성을 빠르게 이해하기 쉬움
  • 이 방식이 유일한 해법은 아니지만 좋은 출발점이 될 수 있음
  • Haskell 커뮤니티 안에서도 오류 처리 방식은 일관적이지 않아 초보자에게 혼란스러울 수 있음

내 코드에 적용하는 방법

  • 준비된 F# 라이브러리가 필요하면 NuGet과 함께 쓸 수 있는 Chessie project를 확인할 수 있음
  • 이 기법을 적용한 샘플 웹 서비스는 GitHub 프로젝트로 제공됨
  • ROP 접근을 FizzBuzz에 적용한 예제는 Railway Oriented Programming: carbonated에서 볼 수 있음
  • F#에는 type class가 없어 모나드를 재사용하는 일반적 방법이 없음
  • FSharpX library는 유용한 접근을 제공하지만, Rop.fs 라이브러리는 모든 함수를 처음부터 직접 정의함
  • 이렇게 격리하면 외부 의존성이 전혀 없다는 장점이 있음

더 읽을거리

댓글과 토론

Hacker News 의견들
  • Elixir는 with 키워드/매크로로 이 문제를 꽤 깔끔하게 다룸
    with {:ok, file_handle} <- File.open(filename), ... do {:ok, parsed} end처럼 위에서 아래로 함수를 실행하고, 각 반환값이 왼쪽 패턴과 맞지 않으면 그 값으로 조기 반환
    그래서 각 함수가 {:ok, value}{:error, reason}을 모두 받도록 만들 필요 없이, 함수는 관심 있는 값만 받고 with 블록의 패턴 매칭이 오류 전파를 처리하게 둘 수 있음
    예를 들어 File.open{:error, reason}을 반환하면 IO.read는 실행되지 않고 전체 with 결과가 {:error, reason}이 됨. 결국 성공 경로만 작성하고 실패 경로는 호출자가 원하면 매칭하게 할 수 있음

    • “매칭되지 않은 것을 조기 반환한다”면, 그 값들이 모두 다른 타입일 때는 어떻게 되는지 궁금함. 동적 타입이라면 어떤 타입인지 보고 어디서 실패했는지 추측해야 하는 것 아닌가 싶음
      예외 처리를 절반쯤 다시 발명하는 느낌이고, 차라리 catch 대신 else 같은 이름을 붙이면 예외 처리처럼 덜 보일 뿐임
    • 이건 아는 한 Haskell의 where와 Lisp의 let에서 영향을 받은 형태임
      함수의 전제조건을 설정하는 셈이고, Elixir에서는 추가로 else 블록까지 쓸 수 있음
    • 이게 예외보다 나은 점이 무엇인지 궁금함. 그리고 IO.read가 실패하면 파일 핸들은 누가 닫는지도 문제임
  • 저자는 몇 년 뒤 Against Railway Oriented Programming이라는 후속 글도 썼음: https://fsharpforfunandprofit.com/posts/against-railway-orie...
    철도 지향 프로그래밍은 흥미롭고 쓸 곳도 있지만, 큰 주의 문구가 필요하다고 봄. 실제로는 예외 처리를 나쁘게 재발명하는 데 쓰이는 경우를 자주 봤고, 예외를 제대로 이해하고 쓰면 대부분의 오류 조건을 더 깔끔하고 효과적으로 처리할 수 있음
    예외의 장점은 대부분의 경우 안전한 선택이 기본값이라는 점임. 오류 조건은 코드가 명세대로 할 수 없다는 신호이고, 그 상태에서 계속 진행하면 잘못된 가정으로 데이터가 손상될 수 있음. 예외는 기본적으로 정리 후 호출자에게 전파되지만, 철도 지향 프로그래밍 같은 방식은 잊기 쉽고 틀리기 쉬운 보일러플레이트를 많이 요구함
    다만 두 가지 경우에는 유용함. 하나는 검증처럼 특정하고 잘 정의된 예상 오류를 발생 지점에서 처리해야 하는 경우이고, 다른 하나는 예외 처리를 쓸 수 없는 환경임. 예전 jQuery의 Promise 기반 비동기 코드는 거의 철도 지향 프로그래밍 구현에 가까웠지만, 현대 JavaScript의 async/await에서는 다시 예외 처리를 쓸 수 있음

    • 주류 언어의 예외에서 아쉬운 점은 이 처리가 보이지 않게 일어난다는 점임. 어떤 함수 안에서 어떤 예외가 나올 수 있는지 컴파일 시점에 파악하기가 쉽지 않음
      철도 지향 프로그래밍의 큰 보상은 함수 시그니처만 봐도 어떤 오류가 나올 수 있는지 즉시 알 수 있다는 것임
      보일러플레이트는 언어에 따라 완화할 수 있음. Haskell에는 do 표기법이 있고, F#에서는 result 계산식으로 매우 깔끔하게 쓸 수 있음. 예를 들어 LoginError = InvalidUser | InvalidPwd | Unauthorized of AuthError처럼 정의하고, Result.requireSome, Result.requireTrue, Result.mapError로 흐름을 구성할 수 있음
      반대로 예외의 단점도 완화할 수 있을지 궁금함. Java IDE와 통합되어 특정 코드 줄에서 던져질 수 있는 잡히지 않은 예외를 자동으로 보여주는 린터나 정적 분석 도구가 있는지 알고 싶음
      https://demystifyfp.gitbook.io/fstoolkit-errorhandling/fstoo...
    • 전통적인 예외 구현 방식은 꽤 별로라고 봄
      우선 많은 언어가 예외를 잡기 위해 어색하고 불필요한 스코프를 강제함. 예외를 던질 수 있는 함수의 결과로 변수를 선언·초기화하고, 실패 시 다른 값을 넣고 싶어도 선언과 초기화를 분리해야 하는 식임
      더 큰 문제는 오류 조건을 올바르게 처리할 충분한 맥락이 있는 가장 낮은 수준에서 처리해야 하는데, 예외의 기본값은 “맨 위까지 던지기”라는 점임. 파일 캐시 하나를 추가했더니 상위 HandleRequest 함수가 갑자기 I/O 예외를 던질 수 있게 되는 식으로 추상화가 새기 쉬움
      함수가 반환할 수 있는 모든 것은 명시적으로 시그니처의 일부여야 하고, 호출자는 명시적으로 처리하거나 위로 넘긴다는 표시라도 해야 한다고 봄. 언어가 이를 위해 많은 보일러플레이트를 요구할 필요는 없음
    • Rust는 일급 문법 지원이 있는 철도 지향 프로그래밍이 보일러플레이트를 거의 없앨 수 있음을 잘 보여줬다고 봄. 개인적으로 Rust의 Result 타입/트레이트는 오류 처리를 제대로 해냈음
    • 값으로서의 오류와 예외의 차이는 도메인별 오류를 설명할 때 가장 유용하다고 봄. 도메인 오류는 값으로 표현하는 게 맞고, 데이터베이스가 연결되지 않는 일처럼 도메인과 무관한 문제는 예외가 더 자연스러움
      파일이나 레코드가 없을 때도 기대 여부에 따라 다름. 사용자가 준 ID로 조회하는 요청이라면 그 조회 자체가 사용자 입력 검증임. 하지만 데이터베이스 레코드에 저장된 blob 이름으로 스토리지를 조회했는데 blob이 없다면, 그건 예외로 처리하기 좋은 상황임
      오류와 예외의 경계는 흐릿하고 팀과 문제 영역에 따라 달라지지만, 이 정도가 괜찮은 경험칙이라고 봄
    • 시스템 수준 오류를 제외하면, “발생 지점에서 처리해야 하는 구체적이고 잘 정의된 예상 오류”가 아닌 오류 상태의 예를 알고 싶음. 그런 경우 예외가 더 나은 이유가 문제의 성격 때문인지, 런타임 제약 때문인지 이해하고 싶음
      C++ 코드에서는 초기에 데이터를 검사하고, 검사에 실패하면 애플리케이션을 크래시시키고, 발견된 오류 상태로 초기 검사 코드를 디버그·수정하는 식으로 처리할 수 있음
      다루는 데이터가 알려진 CAD 데이터 형식이나 기하 위상에 따르는 비교적 명확한 데이터라서, 올바른 데이터가 무엇인지 먼저 이해할 수 있다는 점에서 오류 조건을 다루기가 “꽤 쉬운” 편임
  • 솔직히 이 사이트 전체가 보물창고임. 함수형 언어를 쓸 생각이 없어도 다른 관점을 주고, 그게 개인적으로 큰 도움이 됐음. 다른 글들도 추천함

    • 처음부터 파서 조합기를 만드는 시리즈는 읽고 따라 해본 것 중 가장 가치 있는 자료였음. 거기서 나온 많은 개념과 메커니즘이 타입 있는 언어를 함수형 스타일로 다룰 때 엄청나게 유용했음
      그래도 모나드가 뭔지는 끝내 모를 것 같음
      https://fsharpforfunandprofit.com/series/understanding-parse...
  • “오류 처리를 호출 지점에서 멀리 밀어내는 건 나쁘다”는 주장이 성립하는지 궁금함. 호출자가 오류를 가장 잘 처리할 수 있으니 아래로 넘기기보다 직접 처리해야 하는 것 아닌가 싶음
    예를 들어 validate and-then update-db and-then send-email 흐름에서 validate 실패는 호출자에게 반환해도 괜찮을 수 있음. 하지만 update-db가 실패하면 재시도할지, 다른 서비스를 쓸지, 큐에 다시 넣을지, 사용자에게 알릴지 결정해야 함. send-email 실패도 마찬가지임
    결국 ValidationError는 사용자에게 알리고, DBError는 5분 뒤 재시도하거나 원격 재시도 후 알리고, EmailFailed는 이메일을 다시 큐에 넣고 성공으로 보는 식의 더 복잡한 분기 구조가 되는 것 아닌가 싶음. 어쩌면 그게 괜찮을 수도 있음

    • 모나드식 오류 처리는 서로 장점을 얻는 방식임
      Go를 싫어하는 사람은 오류 조건을 너무 자주 확인해야 한다고 불평하고, Java의 검사 예외도 비슷함. Either를 쓰면 확인을 강제받지 않고, 직접 확인하거나 호출자에게 넘길 수 있음
      Java의 비검사 예외를 싫어하는 사람은 무엇이 언제 던져질지 알 수 없다고 불평함. Either를 쓰면 이게 명시됨
      핵심은 오류가 났을 때 무엇을 해야 할지 모른다는 점이고, Either는 그 모름에 잘 맞음. 현재 Java 코드베이스에서는 Limit getUserLimit(User)가 없을 때 NotFound 예외를 던질 수도, null을 반환할 수도, 기본값을 반환할 수도 있는데, 코드를 파고들기 전에는 알 수 없음
      Either였다면 호출자가 내부 코드를 읽지 않고도 신뢰할 수 있고, orElse(null)이나 orElseThrow(...) 같은 내장 도구로 다른 실패 조건으로 쉽게 바꿀 수 있음. 또한 Either는 표현식이라 추상화하기 좋아서, DbUpdater 내부에 특화된 재시도 코드 대신 일반적인 재시도 로직을 작성할 수 있음
    • 이 논리를 Java의 try-catch 스타일로 구현해봤는데, 중첩된 try, catch(DBError), sleep(5), 원격 재시도, EmailFailedError 재큐잉, ValidationError 알림이 뒤엉켜 훨씬 나빠 보임
      https://gist.github.com/Andrewp2/9d97bd213b061166d6df565ce26...
      누군가 더 나은 try-catch 버전을 쓸 수는 있겠지만, 제시된 철도식 버전보다 훨씬 별로라고 봄
    • 낮은 수준과 높은 수준의 오류 처리는 공존할 수 있지만, 이런 스타일은 칠판 예제나 한 번 만들고 끝나는 상황에서 더 잘 맞는다고 봄
      DDD는 좋아하지만, 나 같은 평범한 프로그래머가 관리할 수 있는 합리적인 유지보수 주기가 필요함. 변경 가능한 코드에서 TDD가 좋았던 건 유지보수의 인지 부하를 줄여줬기 때문임
      철도식 프로그래밍은 많은 변환을 지나며 많은 맥락을 운반할 수 있고, 선로 끝에서 준비되지 않은 비즈니스·시스템 맥락이 잔뜩 실린 기차를 받는 상황은 피하고 싶음
      DDD와 양파 아키텍처가 양쪽 장점을 준다고 봄. 결국 철도는 생기지만, 마이크로 앱 내부에 박히는 게 아니라 바깥에 있음. 철도식 이슈는 명시적이고 약간 번거롭게 코딩되는 편이 낫고, 잘했다면 대부분의 철도식 결정은 영리한 코딩 구조로 피해서는 안 되는 비즈니스 결정
      모나드식 프로그래밍은 훌륭하지만 필요한 비즈니스 결정을 피하는 데 쓰고 싶은 유혹이 있음. 모호함을 타입 시스템에 넣으면 나중에 어렵거나 불가능한 상황으로 이어질 수 있음
    • 낮은 수준과 높은 수준의 오류 처리는 공존할 수 있음. 큰 try/catch 안에 특화된 try/catch가 있는 것과 다르지 않음
      예를 들어 send-email 함수 내부에서 매우 빠른 일시적 오류를 처리하도록 선택할 수 있음. 허용 시간 안에 연결되면 성공 경로로 돌아가고, 아니면 호출자에게 반환하면 됨
      확실한 규칙이 있다면, 사람의 개입이 필요한 처리는 반드시 감싸서 위로 넘겨야 한다는 것임. 성공 경로로 돌아가더라도 경고 로그는 남기는 편이 좋음
    • 제시한 예처럼 끝날 수도 있지만, 같은 기본 최상위 파이프라인을 유지하면서 다르게 구성할 수 있음
      notify-userValidationErrorDBError별 사용자 알림을 담당하고, try-update-dbupdate-db, 5분 뒤 재시도, 원격 재시도를 묶고, try-send-email은 실패 시 이메일을 다시 큐에 넣고 성공으로 바꿀 수 있음
      그러면 최상위는 validate and-then try-update-db and-then try-send-email or-then notify-user처럼 유지됨. and/or/thenmap/bind에 맞게 잘 옮긴 것 같음
  • 이 사이트는 실제적이고 실용적인 개념을 가르친다는 점에서 접한 프로그래밍 교육 사이트 중 최고였음. 특히 잘못된 상태를 표현 불가능하게 만들기 개념이 마음에 들고, 언어와 상관없이 적용하려고 함. 물론 어떤 언어는 더 쉽게 해줌
    https://fsharpforfunandprofit.com/posts/designing-with-types...

  • 호출/반환 대신 데이터 흐름을 쓰면 이 문제는 더 잘 해결되고, 상당 부분 저절로 사라짐
    호출/반환에서는 무언가를 반환해야 하므로 오류 때문에 반환할 값이 없으면 오류를 반환하거나, Go처럼 오류와 정상 반환값을 함께 반환해야 함. 이게 성공 경로를 오염시킴. 다형적 컨테이너로 나머지 처리를 꿰어 넘기면 조금 나아지지만 문제는 남아 있음
    데이터 흐름에서는 다음 필터 단계로 아무것도 보내지 않으면 되므로 성공 경로가 전혀 영향을 받지 않음. 오류는 표준 오류 출력 같은 곳으로 보내고, 애플리케이션 수준에서 중앙화할 수 있음
    너무 좋아 보일 수 있지만 Wunderlist에서 실제로 썼고 잘 동작했음. 값이 없을 때 쓰는 같은 기법은 아직 값이 없을 때, 즉 비동기 처리에도 그대로 적용됨

    • 이게 데이터를 반환하거나 다른 곳에서 잡히도록 예외를 던지는 것과 근본적으로 다른지 궁금함
    • 이해를 돕기 위한 코드 예제가 있으면 좋겠음
    • 그 방식에도 단점이 있고, 특히 제한적이라고 느낌. 예외 처리, 특히 비동기 예외 처리는 오류 복구를 어렵게 만들 수 있음
      단순한 데이터 스트림에서 오류가 나면 다음 단계로 데이터가 없으니 멈추는 건 괜찮음. 하지만 sqrt(-1)에서 바로 “크래시”해 오류 출력이나 로깅으로 건너뛰면, 특정 사용 사례에서는 음수에 대해 다른 값을 주도록 도메인을 넓히고 싶을 때 어려워짐
      이때 Maybe 같은 명시적 오류 타입이 유용함. 성공 경로는 오류가 없는 것처럼 작성하되, 함수 합성에는 다른 연산자를 씀. 오류에서 복구하고 싶으면 그 자리에서 양쪽 경로를 처리하고, 그 이후에는 오류를 허용하지 않을 수도 있음. 추상적인 “있을 수도 있는 값”에서 실제 값을 꺼내려면 양쪽 경로를 우아하게 처리해야 한다는 점이 특히 좋음
      명시적 오류 타입을 쓰면 성공/오류 이분법도 벗어날 수 있음. “값은 없지만”, “값은 있지만” 같은 상태도 표현할 수 있고, 병렬로 처리할 수 있는 여러 작업에서 오류를 누적해 결과로 모을 수도 있음. 더 나아가 비결정성을 인코딩해 그래프 순회 같은 데 쓸 수도 있음
      Haskell 프로그래머들이 하듯 상태를 순수한 방식으로 운반할 수도 있고, 악명 높은 Haskell의 IO 모나드는 실제 경로를 표현한다기보다 외부 세계와 접촉하는 지점을 main으로 제한하는 장치에 가까움. 순수성을 위해서지만, 순수성이 항상 절대적으로 추구해야 할 가치는 아니라고 봄
      이 기법은 단순한 철도보다 훨씬 다양하고, 순수성을 유지하면서 성공 경로만 쓰는 깔끔한 방법을 제공함. 방정식 기반 정적 분석과 목 없는 단위 테스트에도 유리함. 더 많은 주류 언어가 이런 모나드 도구상자를 더 잘 제공했으면 좋겠음
  • 관련 링크들임: Railway Oriented Programming - https://news.ycombinator.com/item?id=31404643 - 2022년 5월, Railway-Oriented Programming (2015) - https://news.ycombinator.com/item?id=17337155 - 2018년 6월, Railway oriented programming - https://news.ycombinator.com/item?id=11955917 - 2016년 6월, Railway Oriented Programming - https://news.ycombinator.com/item?id=9166943 - 2015년 3월, Railway-oriented programming - https://news.ycombinator.com/item?id=7887134 - 2014년 6월
    추가로 What is railway oriented programming? (2020) - https://news.ycombinator.com/item?id=34245639 - 2023년 1월, Railway Oriented Programming in Elixir (2015) - https://news.ycombinator.com/item?id=11958578 - 2016년 6월

  • 이 스타일은 다른 모나드처럼 전염성이 있어서 모든 비즈니스 로직이 이런 모양이 됨. 그게 괜찮다면 계속 읽으면 됨
    예외는 이름 그대로 예상치 못한 오류이고, 잡는 곳까지 스택을 되감으며 필요한 정리는 자동으로 처리됨. I/O 오류나 메모리 부족처럼 비즈니스 함수의 범위 밖에서 발생하는 오류에 적합함
    데이터를 검증하고 싶다면 예외 처리기나 모나드에 의존하지 않아도 됨. 함수형 파이프라인의 결과를 데이터로 만들면 됨. 검증 오류가 있으면 집합으로 모으고, 파이프라인이 끝난 뒤 확인하면 됨. 보통 첫 번째 오류에서 실패시키고 싶지 않을 가능성이 큼
    파이프라인에서 부수 효과가 필요하다면 하지 말고, 그 효과를 데이터로 기술한 뒤 나중에 실행하면 됨. 중간에 일찍 빠져나가야 한다면 그 지점에서 파이프라인을 나누고 결과를 처리한 뒤 다른 파이프라인에 넣으면 됨
    아니면 인터셉터 체인을 써서 성공 경로를 계속할지 결정하게 할 수도 있음. 언어가 지원하면 패턴 매칭을 쓰면 됨. 중요한 건 파이프라인 일부를 건너뛸지 말지의 결정이 비즈니스 함수 바깥에서 이뤄진다는 점이고, 그러면 비즈니스 함수는 순수한 데이터 변환에만 신경 쓰므로 재사용 가능성이 높아짐

  • 이런 주제가 다시 올라와 논의되는 것만으로도 좋음. 이 사이트를 1년 동안 읽고 예제를 따라 했고, 최고의 F# 자료 중 하나였음
    이 사이트에서 다루는 개념들만으로도 모든 언어에서 함수형 사고를 하는 데 도움이 됐음
    오래된 사이트인데 지금 관심을 받는다면 F#이 탄력을 얻고 있다는 뜻인지 궁금함. 그리고 다른 언어 프로그래머들도 Rust처럼 철도식 오류 처리를 쓰는지 궁금함

    • 데스크톱 애플리케이션용 C# 배경이 강하고, 시간이 갈수록 C# 코딩 스타일이 더 함수형이 되어감. Linq를 많이 쓰고, 불변 클래스와 상세 오류 정보를 담을 수 있는 반환 타입을 쓰는 식임
      그래도 F#으로 넘어가지 못하는 이유는 대부분의 시간을 데스크톱 프런트엔드 조정에 쓰기 때문임. 이 영역에서는 F#이 별 이점을 주지 않는 것 같음. 프로덕션 준비가 된 데스크톱 프런트엔드는 네이티브 F#이 아니라 대부분 C# 기반의 객체지향 코드이고, F#과는 얇은 글루 코드로 묶임
      조금이라도 복잡한 UI 맞춤화가 필요해지면 다시 객체지향 세계로 돌아가야 하고, 그럴 바에는 C#에서 바로 하는 편이 낫다고 봄. 개인적으로 F#이 탄력을 얻으려면 네이티브 UI 프레임워크가 필요함
    • C++에서도 그런 방식을 씀. 반환 타입 T와 결과 조건을 정의하는 특정 열거형으로 매개변수화할 수 있는 템플릿 반환 타입을 정의함
      급하면 std::pair로도 할 수 있고, 오류 문장을 문자열에 담아 반환한 뒤 문자열이 비어 있지 않으면 조기 종료하는 방식도 가능함. 모든 함수가 그런 타입을 반환하면 오류 문장을 최상위까지 버블업할 수 있음
  • 이 글이 몇 년마다 다시 올라오는 게 좋음. 정말 훌륭한 글이자 발표임
    다시 읽을 때마다 내 기술 여정에서 실력이나 철학 면에서 다른 위치에 있어서, 매번 다르게 보이는 점도 흥미로움

    • 이런 자료가 정말 좋음. Python 발표 중 “so you want to be a python expert”라는 발표를 초기에 몇 년 동안 6개월마다 봤는데, 볼 때마다 더 많이 이해하게 됐음. 지금 다시 봐도 아마 새로 얻을 게 있을 것 같음