Translate

sábado, 25 de mayo de 2024

Para proteger un tipo de datos en erlang


Los tipos de datos básicos de Erlang son fáciles de detectar visualmente: las tuplas tienen llaves, las listas tienen corchetes, las cadenas están entre comillas dobles, etc. Por lo tanto, hacer cumplir un determinado tipo de datos ha sido posible con la coincidencia de patrones: una función head/1 tomar una lista solo podría aceptar listas porque de lo contrario, la coincidencia ([H|_]) habría fallado.

Sin embargo, tuvimos un problema con los valores numéricos porque no podíamos especificar rangos. En consecuencia, utilizamos guardias en funciones sobre la temperatura, la edad para conducir, etc. Ahora nos topamos con otro obstáculo. ¿Cómo podríamos escribir una protección que garantice que los patrones coincidan con datos de un tipo específico, como números, átomos o cadenas de bits?

Hay funciones dedicadas a esta tarea. Tomarán un único argumento y devolverán verdadero si el tipo es correcto, falso en caso contrario. Son parte de las pocas funciones permitidas en las expresiones de protección y se denominan BIF de prueba de tipo:


is_atom/1           is_binary/1        

is_bitstring/1      is_boolean/1        is_builtin/3       

is_float/1          is_function/1       is_function/2      

is_integer/1        is_list/1           is_number/1        

is_pid/1            is_port/1           is_record/2        

is_record/3         is_reference/1      is_tuple/1         


Se pueden utilizar como cualquier otra expresión de guardia, siempre que se permitan expresiones de guardia. Quizás te preguntes por qué no existe una función que simplemente proporcione el tipo del término que se está evaluando (algo parecido a type_of(X) -> Type). La respuesta es bastante simple. Erlang se trata de programar para los casos correctos: solo programa para lo que sabe que sucederá y lo que espera. Todo lo demás debería provocar errores lo antes posible. Aunque esto pueda parecer una locura, es de esperar que las explicaciones que obtendrá en Errores y excepciones aclaren las cosas. 

Los BIF de prueba de tipo constituyen más de la mitad de las funciones permitidas en las expresiones de protección. El resto también son BIF, pero no representan pruebas de tipo. Estos son:
abs(Number), bit_size(Bitstring), byte_size(Bitstring), element(N, Tuple), float(Term), hd(List), length(List), node(), node(Pid|Ref|Port), round(Number), self(), size(Tuple|Bitstring), tl(List), trunc(Number), tuple_size(Tuple).

Las funciones nodo/1 y self/0 están relacionadas con Erlang distribuido y procesos/actores. Eventualmente los usaremos, pero todavía tenemos otros temas que cubrir antes de eso.

Puede parecer que las estructuras de datos de Erlang son relativamente limitadas, pero las listas y tuplas suelen ser suficientes para construir otras estructuras complejas sin preocuparse por nada. Como ejemplo, el nodo básico de un árbol binario podría representarse como {nodo, Valor, Izquierda, Derecha}, donde Izquierda y Derecha son nodos similares o tuplas vacías. También podría representarme como:

{person, {name, <<"Fred T-H">>},
{qualities, ["handsome", "smart", "honest", "objective"]},
{faults, ["liar"]},
{skills, ["programming", "bass guitar", "underwater breakdancing"]}}.

Lo que muestra que al anidar tuplas y enumerarlas y llenarlas con datos, podemos obtener estructuras de datos complejas y construir funciones para operar con ellas.

En la versión R13B04 se agregó el BIF binario_to_term/2, que le permite deserializar datos de la misma manera que lo haría binario_to_term/1, excepto que el segundo argumento es una lista de opciones. Si pasa [seguro], el binario no se decodificará si contiene átomos desconocidos o funciones anónimas, lo que podría agotar la memoria.