5 de agosto de 2013

Infectado por exceções

Acredito que códigos infectados por exceções sejam ruins. Imagine o seguinte código inofensivo:

public static Foo newFoo(int bar) {
  switch (bar) {
    case 1: 
      return new FooOne();

    case 2: 
      return new FooTwo();

    default: 
      throw new IllegalArgumentException("invalid bar code: " + bar);
  }
}

Você utiliza ele no meio da sua regra de negócio, com a maior tranquilidade. O código compila e tudo:

public static List<BazFoo> getAllBazFoo() {
  List<BazFoo> result = new ArrayList<>();
  List<Baz> allBaz = BazDAO.getAllBaz();
  for (Baz baz : allBaz) {
    int id = baz.getId();
    int bar = baz.getBar();
    Foo foo = Foo.newFoo(bar);
    BazFoo e = new BazFoo(id, foo);
    result.add(e);
  }
  return result;
}

O problema é que a chamada Foo.newFoo(bar) pode lançar um IllegalArgumentException e quando isto ocorrer você não terá pista nenhuma de qual Baz está com a informação inválida.

Para tratar isto e tornar a mensagem mais útil, temos que lembrar de adicionar um try/catch no código:

public static List<BazFoo> getAllBazFoo() {
  List<BazFoo> result = new ArrayList<>();
  List<Baz> allBaz = BazDAO.getAllBaz();
  for (Baz baz : allBaz) {
    int id = baz.getId();
    int bar = baz.getBar();
    Foo foo;
    try {
      foo = Foo.newFoo(bar);
    } catch (IllegalArgumentException e) {
      throw new IllegalArgumentException("invalid baz: " + id, e);
    }
    BazFoo e = new BazFoo(id, foo);
    result.add(e);
  }
  return result;
}

Adicione várias camadas de abstração à sua aplicação e pronto: espagueti sabor excepcional. Ao adicionar exceção em algum ponto, você terá que revisar toda a pilha de abstrações que você tem e garantir que a exceção não faça mal a ninguém e que tenha uma mensagem útil para futuras manutenções.

Haskell resolve boa parte da necessidade de exceções com retornos que simbolizam falhas, e.g. Maybe e Either, e o uso de Monad elimina a necessidade de verificar os retornos a cada passo.

Porém, ainda existem exceções em Haskell, por quê? Não consigo entender o motivo para preferir exceções ao invés de retornos especiais. Em quais situações usar exceções é melhor? Mais me parecem um goto modernizado.