openlang

use 체인 줄마다 붙는 `result.map_error`

use와 result.try 조합은 Gleam에서 제일 좋아하는 부분인데, 요즘 조금 미워지기 시작했어요. 단계마다 다른 모듈을 부르고 모듈마다 자기 오류 타입이 있으니, 줄마다 result.map_error를 달고 다녀요.

pub fn load(id: Int) -> Result(Profile, AppError) {
  use row <- result.try(db.fetch(id) |> result.map_error(DbError))
  use raw <- result.try(decode(row) |> result.map_error(DecodeError))
  use profile <- result.try(validate(raw) |> result.map_error(Invalid))
  Ok(profile)
}

생성자를 함수처럼 넘기니까 짧긴 해요. 그래도 줄마다 절반이 배관이고, 정작 중요한 부분인 그 단계가 하는 일은 가운데에 끼어 있어요.

모든 모듈이 가져다 쓰는 거대한 오류 타입 하나는 답이 아니에요. 그게 더 나빠요. 다들 이런 체인을 어떻게 읽기 좋게 유지하세요? 헬퍼를 쓰나요, 모양을 바꾸나요, 아니면 그냥 참고 사나요?

이어지는 대화 1

서로의 근거를 따라
  1. 고유 링크

    오류 출처마다 헬퍼를 하나씩, fn db(r) { result.map_error(r, DbError) }처럼 만들어 봤어요. 짧아졌지만 더 나빠졌어요. db(db.fetch(id))는 말을 더듬는 것처럼 읽혀서, 인라인으로 돌아가고 파이프마다 한 줄씩 줬어요.

같은 질문의 다른 갈래

  1. TaskGroup으로 옮긴 뒤 `except ValueError`가 잡지 못한다

    작은 점수 계산 스크립트를 asyncio.gather에서 asyncio.TaskGroup으로 옮겼더니 대체 경로가 동작하지 않아요. 바꾸기 전에는 가중치 응답이 비어 있으면 가져오는 쪽에서 ValueError가 났고, exc…

    답글 5python
  2. `String::from_utf8`는 실패하면 바이트를 돌려준다

    값을 소비하는 API는 실패했을 때 그 값이 어떻게 되는지 정해야 합니다. String::from_utf8는 Vec<u8>를 값으로 받고, 실패하면 오류가 그것을 소유합니다. 출력은 bad byte at 2, 4 bytes…

    답글 0rust
  3. 불완전한 레코드 뒤에 커서는 어디에 남나요?

    스트리밍 파서의 호출자는 Result로 레코드가 나왔는지 알 수 있습니다. 하지만 파서가 커서를 옮겼는지, 임시 저장소를 건드렸는지, 입력을 그대로 두었는지는 알 수 없습니다. 레코드가 덜 들어왔을 때 중요한 것은 바로 그…