Railway-Oriented Programming (ROP)

Railway-Oriented Programming (ROP) 주요 개념 및 원칙:

함수형 프로그래밍에서 오류를 처리하는 방법론으로, 프로그램 흐름을 "성공"과 "실패"로 나누어 명확히 구분하는 방식입니다.

  1. 성공과 실패의 명확한 구분:

    • ROP에서는 프로그램 흐름을 "성공(Success)"과 "실패(Failure)"라는 두 가지 경로로 나누어 처리합니다. 이는 오류 처리에서 "예외"나 "실패"를 직접 다루기보다, 흐름을 명시적으로 분리하여 코드 가독성 및 안전성을 높입니다.

  2. Result 객체:

    • 각 함수는 결과를 Result 객체로 반환합니다. Result는 두 가지 상태를 가질 수 있습니다:

      • Result.success(value): 함수가 성공적으로 실행되었음을 나타내며, value는 성공적인 결과입니다.

      • Result.failure(error): 함수가 실패했음을 나타내며, error는 실패 원인에 대한 정보를 담고 있습니다.

    • 이 구조를 통해 함수 간의 결과를 연쇄적으로 처리할 수 있으며, 각 함수가 실패한 경우 즉시 처리 흐름을 종료할 수 있습니다.

  3. 연쇄적 함수 호출:

    • 각 함수는 성공적인 결과만을 다음 함수로 전달합니다. 즉, 함수 체이닝(function chaining) 과정에서 실패가 발생하면 즉시 실패를 반환하고, 이후의 함수 호출은 실행되지 않습니다. 이 방식은 프로그램 흐름을 자연스럽고 안전하게 만듭니다.

  4. 빠른 실패(Early Failure):

    • 실패가 발생한 즉시 그 경로를 반환하여, 더 이상 불필요한 작업을 진행하지 않도록 합니다. 이는 프로그램이 잘못된 경로를 계속 따라가지 않게 하여 성능 및 안전성을 강화합니다.

  5. 조합 가능성(Composability):

    • 함수형 프로그래밍의 중요한 특성인 조합 가능성은 ROP에서도 중요한 역할을 합니다. 각 함수가 독립적이고, Result 객체를 통해 결과를 반환하여 다른 함수들과 쉽게 결합될 수 있습니다.

Flutter에서의 ROP 구현 예시

기존 코드 (전통적인 오류 처리)

int divide(int a, int b) {
  if (b == 0) throw Exception('Cannot divide by zero');
  return a ~/ b;
}

void main() {
  try {
    var result = divide(10, 0);
    print('Result: $result');
  } catch (e) {
    print('Error: $e');
  }
}

개선된 코드 (Railway-Oriented Programming)

class Result<T, E> {
  final T? value;
  final E? error;
  final bool isSuccess;

  Result._(this.value, this.error, this.isSuccess);

  factory Result.success(T value) => Result._(value, null, true);
  factory Result.failure(E error) => Result._(null, error, false);
}

Result<int, String> divide(int a, int b) {
  if (b == 0) return Result.failure('Cannot divide by zero');
  return Result.success(a ~/ b);
}

Result<int, String> add(Result<int, String> prevResult, int valueToAdd) {
  if (!prevResult.isSuccess) return prevResult;
  return Result.success(prevResult.value! + valueToAdd);
}

void main() {
  var result = divide(10, 2);
  result = add(result, 5);

  if (result.isSuccess) {
    print('Success: ${result.value}');
  } else {
    print('Error: ${result.error}');
  }
}

Last updated