Railway-Oriented Programming (ROP)
Railway-Oriented Programming (ROP) 주요 개념 및 원칙:
함수형 프로그래밍에서 오류를 처리하는 방법론으로, 프로그램 흐름을 "성공"과 "실패"로 나누어 명확히 구분하는 방식입니다.
성공과 실패의 명확한 구분:
ROP에서는 프로그램 흐름을 "성공(Success)"과 "실패(Failure)"라는 두 가지 경로로 나누어 처리합니다. 이는 오류 처리에서 "예외"나 "실패"를 직접 다루기보다, 흐름을 명시적으로 분리하여 코드 가독성 및 안전성을 높입니다.
Result 객체:
각 함수는 결과를
Result
객체로 반환합니다.Result
는 두 가지 상태를 가질 수 있습니다:Result.success
(value)
: 함수가 성공적으로 실행되었음을 나타내며,value
는 성공적인 결과입니다.Result.failure
(error)
: 함수가 실패했음을 나타내며,error
는 실패 원인에 대한 정보를 담고 있습니다.
이 구조를 통해 함수 간의 결과를 연쇄적으로 처리할 수 있으며, 각 함수가 실패한 경우 즉시 처리 흐름을 종료할 수 있습니다.
연쇄적 함수 호출:
각 함수는 성공적인 결과만을 다음 함수로 전달합니다. 즉, 함수 체이닝(function chaining) 과정에서 실패가 발생하면 즉시 실패를 반환하고, 이후의 함수 호출은 실행되지 않습니다. 이 방식은 프로그램 흐름을 자연스럽고 안전하게 만듭니다.
빠른 실패(Early Failure):
실패가 발생한 즉시 그 경로를 반환하여, 더 이상 불필요한 작업을 진행하지 않도록 합니다. 이는 프로그램이 잘못된 경로를 계속 따라가지 않게 하여 성능 및 안전성을 강화합니다.
조합 가능성(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