El tipo bitstring en Elixir es una herramienta fascinante que nos permite representar números con una longitud específica en bits, algo que no es común en muchos lenguajes modernos. A diferencia de lenguajes como C o Java, donde los números tienen un tamaño fijo —por ejemplo, 2, 4 u 8 bytes— y pueden desbordarse si intentamos almacenar un valor demasiado grande, en Elixir los números pueden ser tan largos como queramos. Sin embargo, con bitstrings podemos imponer una longitud concreta en bits para representar un número, lo que abre posibilidades interesantes, especialmente para quienes trabajan con manipulación a nivel binario o aritmética modular.
Para crear un bitstring, utilizamos una sintaxis particular: dos signos menor que << seguidos de dos signos mayor que >>. Entre estos símbolos colocamos el número que queremos representar, seguido de dos puntos dobles :: y el número de bits que deseamos usar. Por ejemplo, para representar el número 14 con 4 bits escribiríamos:
<<14::4>>
Esto significa que estamos usando 4 bits para almacenar el número 14, lo cual es posible porque 4 bits pueden representar hasta 16 valores diferentes (de 0 a 15). Si intentamos representar un número que exceda la capacidad de bits asignada, como el 16 con 4 bits, el valor se desbordará y solo se conservarán los bits menos significativos, resultando en un valor incorrecto para nuestra intención original. Este comportamiento puede ser útil para ciertos cálculos, como la aritmética modular, donde los valores vuelven a empezar después de un límite.
Además, podemos concatenar varios valores dentro de un bitstring, separándolos por comas. Por ejemplo:
<<17::4, 10::4, 8::4>>
Esto crea un bitstring de 12 bits que contiene tres números representados con 4 bits cada uno. Un detalle curioso es que bitstrings con diferentes divisiones de bits pueden representar el mismo conjunto de bits. Por ejemplo, <<1::2, 3::2>> equivale a <<7::4>> porque ambos representan la secuencia binaria 0111. Esto nos permite comparar bitstrings basándonos en su contenido binario, sin importar cómo estén segmentados.
Un aspecto importante es que Elixir prefiere que los bitstrings tengan un tamaño múltiplo de 8 bits, es decir, un número entero de bytes. Cuando esto ocurre, el bitstring recibe un nombre especial: binary. Por ejemplo, <<25::8>> es un binary, y también <<25::16>> porque 16 es múltiplo de 8. Si el tamaño no es múltiplo de 8, entonces es simplemente un bitstring, pero no un binary. Podemos comprobar esto con las funciones is_bitstring/1 e is_binary/1:
is_bitstring(5) # false, porque 5 es un número, no un bitstring
is_bitstring("5") # true, porque las cadenas son bitstrings
is_binary("hola") # true, porque las cadenas son binaries (bitstrings con tamaño múltiplo de 8)
is_binary(<<5::7>>) # false, porque 7 no es múltiplo de 8
Aquí llegamos a un punto fundamental: las cadenas de texto en Elixir son en realidad bitstrings que cumplen la condición de ser binaries. Esto se debe a que cada carácter en una cadena está representado por un código Unicode, que puede ocupar varios bytes. Por eso, cuando hacemos is_bitstring("hola") o is_binary("hola"), ambas funciones devuelven true.
El hecho de que las cadenas sean bitstrings Unicode tiene implicaciones importantes. A diferencia de lenguajes como C, donde un carácter suele ocupar un byte, en Elixir cada carácter puede ocupar varios bytes para representar cualquier símbolo del vasto conjunto de caracteres Unicode. Esto permite manejar alfabetos muy diversos, desde el ruso o japonés hasta alfabetos menos comunes como el cuneiforme. Por lo tanto, el número de bytes en una cadena no tiene por qué coincidir con el número de caracteres visibles, algo que debemos tener en cuenta si venimos de otros lenguajes.
Podemos inspeccionar la representación interna de una cadena para ver cómo se codifican sus caracteres en números Unicode dentro de un bitstring:
IO.inspect("hola")
# Output: "hola"
IO.inspect(:erlang.bit_size("hola"))
# Output: 32 (4 caracteres * 8 bits)
Esto nos muestra que la cadena hola está compuesta por 4 caracteres, cada uno representado con 8 bits, sumando un total de 32 bits.
En resumen, el tipo bitstring nos ofrece un control muy fino sobre la representación binaria de datos en Elixir, y entenderlo nos ayuda a comprender mejor cómo funcionan internamente las cadenas de texto y otros tipos de datos en este lenguaje. Además, nos abre la puerta a manipulaciones a nivel de bits que pueden ser útiles en contextos específicos, como la aritmética modular o el procesamiento de datos binarios.