Pular para o conteúdo

Resultado e Talvez

A falha como valor: ok/falha com mapear, entao e recuperar; e Talvez para onde void é ambíguo — as três formas de lidar com o que dá errado.

A linguagem tem três formas de lidar com o que dá errado, e elas respondem a perguntas diferentes. Escolher a errada é o que produz código que engole erro ou que interrompe o que devia continuar.

FormaQuandoO que acontece
monitor / handle / triggero que não era esperado: disco cheio, rede caída, buginterrompe e sobe até quem sabe tratar
void com ?? e ?.a ausência simples: campo opcional, cache vaziosegue com um padrão
Arcane.Resultadoa falha esperada de uma fronteira: validação, busca, parsingvira valor, e quem chama decide

ok e falha#

dataforge
adopt Arcane.Resultado as R

action buscar(id) -> Resultado:
    given id smaller 0:
        yield R.falha("id negativo", 400)
    yield R.ok({"id": id, "nome": "Ana"})

achado := buscar(7)
perdido := buscar(-1)

assert achado.deu_certo()
assert achado.valor()["nome"] is "Ana"

assert perdido.falhou()
assert perdido.erro() is "id negativo"
assert perdido.detalhe() is 400
assert perdido.ou("ninguém") is "ninguém" 

A falha atravessa a corrente#

mapear muda o valor, entao encadeia outra operação que também devolve Resultado, e recuperar dá outra chance. A falha passa por todos eles intacta — é isso que dispensa um given entre cada passo.

dataforge
adopt Arcane.Resultado as R

action dobro(x):
    yield R.ok(x * 2)

assert R.ok(2).mapear(lambda x => x + 1).valor() is 3
assert R.ok(2).entao(dobro).valor() is 4

assert R.falha("parou").mapear(lambda x => x + 1).erro() is "parou"
assert R.falha("parou").entao(dobro).erro() is "parou"
assert R.falha("parou").recuperar(lambda motivo => R.ok(0)).valor() is 0

tentar: transformar erro em valor, uma vez#

R.tentar roda a ação e devolve ok ou falha com a mensagem. Ele captura o erro da linguagem — um sinal de controle (halt, skip, yield) atravessa, senão um halt dentro dele viraria falha em vez de sair do laço.

dataforge
adopt Arcane.Resultado as R

action dividir(a, b):
    yield a / b

assert R.tentar(dividir, 10, 2).valor() is 5.0

ruim := R.tentar(dividir, 1, 0)
assert ruim.falhou()
assert "zero" in ruim.erro()

todos: a validação inteira, ou o primeiro motivo#

É o que se quer ao validar um formulário: ou sai a lista pronta, ou sai o motivo — e não uma lista com buracos. R.erros faz o contrário, e reúne todos os motivos para relatar de uma vez.

dataforge
adopt Arcane.Resultado as R

assert R.todos([R.ok(1), R.ok(2), R.ok(3)]).valor() is [1, 2, 3]
assert R.todos([R.ok(1), R.falha("cpf"), R.falha("email")]).erro() is "cpf"
assert R.erros([R.ok(1), R.falha("cpf"), R.falha("email")]) is ["cpf", "email"]

Talvez: quando void é ambíguo#

void resolve a ausência em quase todo lugar. O que ele não resolve é distinguir "a chave não está lá" de "a chave está lá e vale void" — que é exatamente a dúvida de um vault de configuração, de um cache e de uma busca.

dataforge
adopt Arcane.Resultado as R

config := {"tema": void}

tem := R.chave(config, "tema")
nao := R.chave(config, "idioma")

assert tem.tem() and tem.valor() is void      // está lá, e vale void
assert not nao.tem()
assert nao.ou("pt-BR") is "pt-BR"

assert R.algo(2).mapear(lambda x => x * 5).valor() is 10
assert R.nada().mapear(lambda x => x * 5).ou("nada") is "nada"
assert not R.algo(4).filtrar(lambda x => x bigger 10).tem()
assert R.primeiro([1, 2, 3], lambda x => x bigger 2).valor() is 3
assert R.nada().para_resultado("vazio").erro() is "vazio" 

A superfície#

SímboloO que faz
R.ok(valor) · R.falha(erro, detalhe)cria o resultado
R.tentar(acao, …)roda e captura o erro da linguagem
R.de(valor, motivo)void vira falha; o resto vira ok
R.todos(lista) · R.erros(lista)junta, ou reúne os motivos
r.deu_certo() · r.falhou()pergunta
r.valor() · r.erro() · r.detalhe()lê — valor() levanta na falha
r.ou(padrao) · r.exigir(msg)lê sem levantar, ou com a sua mensagem
r.mapear(f) · r.entao(f) · r.recuperar(f)transforma, encadeia, recupera
R.algo(v) · R.nada() · R.talvez(v)cria o Talvez
R.primeiro(colecao, cond) · R.chave(vault, nome)busca que pode não achar
t.tem() · t.valor() · t.ou(p) · t.mapear(f) · t.filtrar(f)o Talvez
t.para_resultado(motivo)vira Resultado