A sugerencia de @ amon, aquí hay una respuesta que es más monádica. Es una versión muy resumida, donde debes aceptar algunas suposiciones:
la función "unidad" o "retorno" es el constructor de la clase
la operación de "vinculación" ocurre en tiempo de compilación, por lo que está oculta a la invocación
las funciones de "acción" también están vinculadas a la clase en tiempo de compilación
Aunque la clase es genérica y envuelve cualquier clase arbitraria E, creo que en realidad es exagerado en este caso. Pero lo dejé así como un ejemplo de lo que podrías hacer.
Con esas consideraciones, la mónada se traduce en una clase de envoltura fluida (aunque está renunciando a la flexibilidad que obtendría en un lenguaje puramente funcional):
public class RepositoryLookup<E> {
private String source;
private E answer;
private Exception exception;
public RepositoryLookup<E>(String source) {
this.source = source;
}
public RepositoryLookup<E> fetchElement() {
if (answer != null) return this;
if (! exception instanceOf NotFoundException) return this;
try {
answer = lookup(source);
}
catch (Exception e) {
exception = e;
}
return this;
}
public RepositoryLookup<E> orFetchSimilarElement() {
if (answer != null) return this;
if (! exception instanceOf NotFoundException) return this;
try {
answer = lookupVariation(source);
}
catch (Exception e) {
exception = e;
}
return this;
}
public RepositoryLookup<E> orFetchParentElement() {
if (answer != null) return this;
if (! exception instanceOf NotFoundException) return this;
try {
answer = lookupParent(source);
}
catch (Exception e) {
exception = e;
}
return this;
}
public boolean failed() {
return exception != null;
}
public Exception getException() {
return exception;
}
public E getAnswer() {
// better to check failed() explicitly ;)
if (this.exception != null) {
throw new IllegalArgumentException(exception);
}
// TODO: add a null check here?
return answer;
}
}
(esto no se compilará ... ciertos detalles se dejan sin terminar para mantener la muestra pequeña)
Y la invocación se vería así:
Repository<String> repository = new Repository<String>(x);
repository.fetchElement().orFetchParentElement().orFetchSimilarElement();
if (repository.failed()) {
throw new IllegalArgumentException(repository.getException());
}
System.err.println("Got " + repository.getAnswer());
Tenga en cuenta que tiene la flexibilidad para componer las operaciones de "búsqueda" como desee. Se detendrá cuando reciba una respuesta o una excepción que no sea encontrada.
Hice esto muy rápido; no está del todo bien, pero con suerte transmite la idea
NotFoundException
algo realmente excepcional?