Я иногда для подобных задач использую что-то вроде (псевдокод, но надеюсь донести идею):
do_something(State) ->
lists:foldl(fun apply_if_ok/2, {ok, State}, [fun transform1/1, fun transform2/1, fun transform3/1]).
apply_if_ok(Function, {ok, State}) -> Function(State);
apply_if_ok(_, {error, State}) -> {error, State}.
transform1(State) -> {ok, State}. % Will be called
transform2(State) -> {error, State}. % Will be called
transform3(State) -> {ok, State}. % Will not be called because transform2 fails
Тут логика «если уже есть ошибка, то дальше ничего не делать» сконцентрирована в специальной функции.
Минус такого подхода в том, что обычно для такой цепочки преобразований нужно городить тип-контейнер, который передавался бы как State, а также в том, что функции с преобразованиями должны иметь строго одинаковый интерфейс (под специальный тип стейта).
Удобство среди прочего в том, что порядок преобразований можно записать в столбик, что позволяет легко комментировать и менять местами шаги.