style="display:inline-block;width:728px;height:90px"
data-ad-client="ca-pub-5164839828746352"
data-ad-slot="7563230308">

Java safe null

Todos tenemos a nuestros maestros en temas de desarrollo, personas que seguimos, quienes siempre están buscando cambiar lo cotidiano, quienes tienen tienen una gran idea o quienes pueden compartir su pasión por algo en particular, por el momento estoy siguiendo al Profesor Frisby, en estas cosas de programación funcional y composición de funciones. Tiene un curso completo de este tema gratuito (como dice mi esposa del verbo “nel no pago”) para javascript, Esta es la liga: Professor Frisby Introduces Composable Functional JavaScript.

Porque como se darán cuenta no soy muy bueno para explicar, pero el si :), y si quieren tener mas detalles del porque los pequeños bloques de código que ultimamente he estado poniendo, en ese curso podran entenderlo mejor o confundirse mas, a saber de cada quien :P.

Bueno regresando al tema, como bien menciono @ezamudio aqui, una de las características de Kotlin que segun ayudan a los programadores a hacer mejor código es el manejo seguro de nulos (y digo según porque el que programa sin saber que esta haciendo, ni las formulas de excel le quedan bien).

Pero eso no quiere decir que no podamos escribir código seguro de nulos por nuestra cuenta, al igual que con Kotlin, no totalmente, nunca nos podremos librar de los nulos a menos que no existan, como en prolog y tengamos que manejar “anything at all”.

Y para eso y con ayuda de nuestras cajas mágicas, vamos a realizar un pequeño programa que busque en una lista de colores en hexadecimal con # (“#fffff”) el color por nombre y nos regrese ese color, le quite el # y lo convierta a mayusculas.

Entonces vamos a usar el famoso tipo Either, el cual permite componer funciones pero ignorando los casos de fallo (nulos) y encadenando correctamente los casos positivos. Primero voy a colocar todo el código, esto simplemente lo ponemos en un archivo que se llame “Either.java”

import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

abstract class LeftRight<T> {
    protected T x;

    protected LeftRight(T x) {
        this.x = x;
    }

    abstract <R> LeftRight<R> map(Function<T, R> f);

    abstract <R> R fold(Function<T, R> f, Function<T, R> g);

    abstract String inspect();
}

class Right<T> extends LeftRight<T> {

    public Right(T x) {
        super(x);
    }

    <R> Right<R> map(Function<T, R> f) {
        return new Right<>(f.apply(this.x));
    }

    <R> R fold(Function<T, R> f, Function<T, R> g) {
        return g.apply(this.x);
    }

    String inspect() {
        return "Right(" + this.x != null ? x.toString() : x + ")";
    }
}

class Left<T> extends LeftRight<T> {

    public Left(T x) {
        super(x);
    }

    Left<T> map(Function f) {
        return new Left(this.x);
    }

    <R> R fold(Function<T, R> f, Function<T, R> g) {
        return f.apply(this.x);
    }

    String inspect() {
        return "Left(" + this.x != null ? x.toString() : x + ")";
    }
}

class FromNullable<T> {
    public LeftRight<T> getNullable(T x) {
        return x == null ? new Left<>(x) : new Right<>(x);
    }
}

class ColorFinder {
    public static String find(String color, Map<String, String> colors) {
        return new FromNullable<String>().getNullable(colors.get(color))
                .map((String x) -> x.substring(1))
                .map((String c) -> c.toUpperCase())
                .fold(e -> "no Color",
                        (String c) -> c);
    }
}

public class Either {

    public static void main(String... args) {
        Map<String, String> colors = new HashMap<>();
        colors.put("red", "#ff4444");
        colors.put("blue", "#3b5998");
        colors.put("yellow", "#fff68f");

        System.out.println(ColorFinder.find("blue", colors));

        System.out.println(ColorFinder.find("green", colors));

    }

}

Nota que tenemos dos cajas una derecha y una izquierda, pero la función (método o como sea) map en Left no aplica la función que recibe y solo regresa el valor del closure (privado) x, mientras que Right aplica la función al valor que recibe f(x), también la función fold cambia, ahora reciben dos funciones f y g, en Left hacemos f(x) por estar a la izquierda y en Right hacemos g(x) por obvias razones.

LeftRight solo es para poder hacer genérico el asunto.

FromNullable es donde esta la magia de esta aproximación, si el dato que tenemos es nulo, vamos a regresar un Left en caso contrario un Right.

class FromNullable<T> {
    public LeftRight<T> getNullable(T x) {
        return x == null ? new Left<>(x) : new Right<>(x);
    }
}

Si bien estamos controlando el nulo aquí, todo nuestro código dependiente de esto es null safe:

class ColorFinder {
    public static String find(String color, Map<String, String> colors) {
        return new FromNullable<String>().getNullable(colors.get(color))
                .map((String x) -> x.substring(1))
                .map((String c) -> c.toUpperCase())
                .fold(e -> "no Color",
                        (String c) -> c);
    }
}

Mira que recibimos un Map y sabemos que get de map puede regresar un nulo, entonces hacemos toda nuestra lógica a partir de un posible nulo (fromNullable), mira que la función map sera aplicada si x no es nula y sera ignorada cuando x sea nula, podremos encadenar (con map) las funciones que queramos y ninguna arrojara nulo porque el camino a seguir siempre sera la izquierda que solo regresa el valor pero no aplica la función, fold mostrara “no Color” cuando nulo y el color en mayusculas cuando no sea nulo.

La salida del código es:

3B5998
no Color

Primero buscamos blue en el HahsMap que si existe y luego green que no existe en el hashmap.

Ahora con Kotlin, no me gusto tanto en Left tener que hacer un casting del tipo T a R, pero no encontré otra manera de hacerlo, si busque :P, bueno el código es muy parecido y la salida es la misma:

interface KLeftRight<T> {
    fun <R> map (f: (x: T) -> R): KLeftRight<R>

    fun <R> fold(f: (x: T?) -> R, g: (x: T) -> R): R

    fun inspect(): String

}

class KRight<T>(private val x: T) : KLeftRight<T> {

    override fun <R> map(f: (x: T) -> R): KLeftRight<R> {
        return KRight(f(x))
    }

    override fun <R> fold(f: (x: T?) -> R, g: (x: T) -> R): R {
        return g(x)
    }

    override fun inspect(): String {
        return "Right($x)"
    }

}

class KLeft<T>(private val x: T) : KLeftRight<T> {

    override fun <R> map(f: (x: T) -> R): KLeft<R> {
        return KLeft(x as R)
    }

    override fun <R> fold(f: (x: T?) -> R, g: (x: T) -> R): R {
        return f(x)
    }

    override fun inspect(): String {
        return "Left($x)"
    }

}

fun <T> getNullable(x: T) : KLeftRight<T> {
    return if(x == null) { KLeft(x) } else { KRight(x) }
}

fun findColor(color: String, colors: Map<String, String>): String {
    return getNullable(colors[color])
            .map { x: String? -> x!!.substring(1) }
            .map { c: String -> c.toUpperCase() }
            .fold({ e -> "no Color" }, { c: String -> c })
}

fun main(args: Array<String>) {
    val colors = hashMapOf("red" to "#ff4444", "blue" to "#3b5998", "yellow" to "#fff68f")

    println(findColor("blue", colors))
    println(findColor("green", colors))
}

Salida:

3B5998
no Color

Y por supuesto lo mismo en javascript, mucho menos código :P

const Right = x => ({
  map: f => Right(f(x)),
  fold: (f, g) => g(x),
  inspect: () => `Rigth(${x})`
});

const Left = x => ({
  map: () => Left(x),
  fold: (f, g) => f(x),
  inspect: () => `Left(${x})`
});

const colors = {
  red: '#ff4444',
  blue: '#3b5998',
  yellow: '#fff68f',
};

const fromNullable = x => x ? Right(x) : Left(x);

const findColor = (color, colors) => fromNullable(colors[color])
    .map(x => x.slice(1))
    .map(x => x.toUpperCase())
    .fold(e => 'No color', x => x);

console.log(findColor('blue', colors))
console.log(findColor('green', colors))

Misma salida

3B5998
No color

Comentarios

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.
Imagen de ezamudio

Optional?

No es más fácil simplemente usar Optional?

Optional.ofNullable(colors.get(color))
                .map(x -> x.substring(1))
                .map(c -> c.toUpperCase())
                .orElse("no Color")

Es correcto

Estas en lo correcto usar optional evita crear los tipos propios, ahora bien, no se si tambien aplique para excepciones ademas de nulos, voy a investigar en eso y si es asi lo escribire en el siguiente post, sin embargo el codigo que coloque sirve para darnos una idea de como inventarnos tipos que tal vez no existan.

Aprovechando el vuelo es bueno mencionar que nuevos lenguajes como Rust estan haciendo uso muy intensivo de este tipo de soluciones para no lidiar con nulos directamente y evitar errores Rust option, Rust result

Gracias por la observacion.

Imagen de ezamudio

Optional

Optional es para evitar NPE nada más, si lo sabes usar. No maneja otros tipos de excepciones, aunque puede generarlos con orElseThrow.

style="display:inline-block;width:728px;height:90px"
data-ad-client="ca-pub-5164839828746352"
data-ad-slot="7563230308">