jueves, 29 de febrero de 2024

Listas por comprensión en Erlang


Las listas por comprensión son formas de crear o modificar listas. También hacen que los programas sean breves y fáciles de entender en comparación con otras formas de manipular listas. Se basa en la idea de notación de conjuntos; Si alguna vez has tomado clases de matemáticas con teoría de conjuntos o si alguna vez has estudiado notación matemática, probablemente sepas cómo funciona. La notación de conjuntos básicamente le dice cómo construir un conjunto especificando las propiedades que deben satisfacer sus miembros. La comprensión de las listas puede ser difícil de entender al principio, pero vale la pena el esfuerzo. 

Un ejemplo de notación de conjuntos sería {x ∈ ℜ x = x^2}. Esa notación de conjuntos le indica que los resultados que desea serán todos números reales iguales a su propio cuadrado. El resultado de ese conjunto sería {0,1}. Otro ejemplo de notación de conjuntos, más simple y abreviado sería {x : x > 0}. Aquí, lo que queremos son todos los números donde x > 0.

Las listas por comprensión en Erlang tratan de construir conjuntos a partir de otros conjuntos. Dado el conjunto {2n : n en L} donde L es la lista [1,2,3,4], la implementación de Erlang sería:

1> [2*N || N <- [1,2,3,4]].

[2,4,6,8]

Si comparamos la notación matemática con la de Erlang y no hay mucho que cambie: las llaves ({}) se convierten en corchetes ([]), los dos puntos (:) se convierten en dos barras verticales (||) y la palabra 'in' se convierte en la flecha. (<-). Sólo cambiamos símbolos y mantenemos la misma lógica. En el ejemplo anterior, cada valor de [1,2,3,4] coincide secuencialmente con el patrón N. La flecha actúa exactamente como el operador =, con la excepción de que no genera excepciones.

También puede agregar restricciones a la comprensión de una lista mediante operaciones que devuelvan valores booleanos. Si quisiéramos todos los números pares del uno al diez, podríamos escribir algo como:

2> [X || X <- [1,2,3,4,5,6,7,8,9,10], X rem 2 =:= 0].

[2,4,6,8,10]

Donde X rem 2 =:= 0 comprueba si un número es par. Las aplicaciones prácticas surgen cuando decidimos que queremos aplicar una función a cada elemento de una lista, obligándolo a respetar restricciones, etc. Como ejemplo, digamos que somos dueños de un restaurante. Un cliente entra, ve nuestro menú y pregunta si podría tener los precios de todos los artículos que cuestan entre $3 y $10 con los impuestos (digamos 7%) contados después.

3> RestaurantMenu = [{steak, 5.99}, {beer, 3.99}, {poutine, 3.50}, {kitten, 20.99}, {water, 0.00}].

[{steak,5.99},

{beer,3.99},

{poutine,3.5},

{kitten,20.99},

{water,0.0}]

4> [{Item, Price*1.07} || {Item, Price} <- RestaurantMenu, Price >= 3, Price =< 10].

[{steak,6.409300000000001},{beer,4.2693},{poutine,3.745}]


Por supuesto, los decimales no están redondeados de manera legible, pero entiendes el punto. Por lo tanto, la receta para la comprensión de listas en Erlang es NewList = [Expression || Patrón <- Lista, Condición1, Condición2, ... CondiciónN]. La parte Patrón <- Lista se denomina expresión Generadora. 


5> [X+Y || X <- [1,2], Y <- [2,3]].

[3,4,4,5]


Esto ejecuta las operaciones 1+2, 1+3, 2+2, 2+3. Entonces, si desea que la receta de comprensión de la lista sea más genérica, obtendrá: NewList = [Expression || GeneradorExp1, GeneradorExp2, ..., GeneradorExpN, Condición1, Condición2, ... CondiciónM]. Tenga en cuenta que las expresiones del generador junto con la coincidencia de patrones también actúan como filtro:

6> Weather = [{toronto, rain}, {montreal, storms}, {london, fog},  
6>            {paris, sun}, {boston, fog}, {vancouver, snow}].
[{toronto,rain},
{montreal,storms},
{london,fog},
{paris,sun},
{boston,fog},
{vancouver,snow}]
7> FoggyPlaces = [X || {X, fog} <- Weather].
[london,boston]

Si un elemento de la lista 'Clima' no coincide con el patrón {X, niebla}, simplemente se ignora en la comprensión de la lista, mientras que el operador = habría generado una excepción.

lunes, 26 de febrero de 2024

Imports en gleam


// Import the module and one of its functions

import gleam/io.{println}


pub fn main() {

  // Use the function in a qualified fashion

  io.println("This is qualified")


  // Or an unqualified fashion

  println("This is unqualified")

}


Normalmente, las funciones de otros módulos se utilizan de forma calificada, con el calificador del módulo antes del nombre de la función. Por ejemplo, io.println("¡Hola!").

También es posible especificar una lista de funciones para importar desde un módulo de forma no calificada, como la función println en el editor de código. Debido a que se importó de esta manera, se le puede llamar simplemente println.

Generalmente es mejor utilizar importaciones calificadas, ya que esto deja claro dónde está definida la función, lo que hace que el código sea más fácil de leer.

Hello world en Gleam

 


// Import a Gleam module from the standard library

import gleam/io


pub fn main() {

  // Print to the console

  io.println("Hello world!")

}


Aquí hay un programa que imprime el texto "Hello world!".

Para ello, utiliza la función println que se ha importado del módulo gleam/io, que forma parte de la biblioteca estándar de Gleam.

Para ejecutar este programa se usa el comando gleam run.


domingo, 25 de febrero de 2024

Instalando go zero

 


Ya hemos hablado de zero el framework de go para hacer microservicios. Ahora vamos a bajar las herramientas para trabajar con él. 

Primero, tenemos que tener go instalado. Yo tengo la versión 1.22.0

$ go version

go version go1.22.0 linux/amd64


Después de la versión 1.11, se recomienda que el valor GO111MODULE se establezca en on para evitar errores innecesarios.

$ go env -w GO111MODULE=on
$ go env -w GOPROXY=https://goproxy.cn,direct

$ go env GO111MODULE
on
$ go env GOPROXY
https://goproxy.cn,direct

goctl es una herramienta incorporada de go-zero que es importante para aumentar la eficiencia del desarrollo, generar código, documentos, implementar k8s yaml, dockerfile, etc.

$ go install github.com/zeromicro/go-zero/tools/goctl@latest
go: downloading github.com/gookit/color v1.5.4
go: downloading github.com/withfig/autocomplete-tools/integrations/cobra v1.2.1
go: downloading github.com/spf13/cobra v1.8.0
...

$ goctl -version
goctl version 1.6.2 linux/amd64

Ahora tenemos que installar docker. Yo ya tengo docker instalado por lo tanto siguo: 

$ sudo docker pull kevinwan/goctl
$ sudo docker run --rm -it -v `pwd`:/app kevinwan/goctl goctl --help

Y si todo anda bien hacemos : 

$ sudo docker run --rm -it -v `pwd`:/app kevinwan/goctl:latest goctl --version
goctl version 1.3.5 linux/amd64

protoc es una herramienta para generar código basado en archivos proto, este genern código en múltiples lenguajes como C++, Java, Python, Go, PHP, etc. para nuestros servicios gRPC. Lo tenemos que instalar, en mi caso yo lo tenia instalado y lo sé porque hice : 

$ goctl env check --install --verbose --force
[goctl-env]: preparing to check env

[goctl-env]: looking up "protoc"
[goctl-env]: "protoc" is installed

[goctl-env]: looking up "protoc-gen-go"
[goctl-env]: "protoc-gen-go" is installed

[goctl-env]: looking up "protoc-gen-go-grpc"
[goctl-env]: "protoc-gen-go-grpc" is installed

[goctl-env]: congratulations! your goctl environment is ready!

Y ahora por fin, vamos a crear nuestro proyecto go-zero!! 

Creamos el proyecto: 
$ mkdir myapp && cd myapp
$ go mod init myapp
$ go get github.com/zeromicro/go-zero@latest
go: added github.com/zeromicro/go-zero v1.6.2


Y Listo!! 


sábado, 24 de febrero de 2024

Gleam Language Tour


Como tengo tanto tiempo y siempre viene bien aprender un nuevo lenguaje, voy a hacer el Gleam Language Tour. 

Ya he hablado de Gleam en otro post y no me quiero repetir. Les comparto el link y los invito a hacerlo conmigo. 

Dejo link: https://tour.gleam.run/basics/hello-world/

jueves, 22 de febrero de 2024

Listas en Erlang


Las listas se utilizan para resolver todo tipo de problemas y son sin duda la estructura de datos más utilizada en Erlang. ¡Las listas pueden contener cualquier cosa! Números, átomos, tuplas, otras listas. La notación básica de una lista es [Elemento1, Elemento2,..., ElementoN] y puedes mezclar más de un tipo de datos en ella:


> [1, 2, 3, {numbers,[4,5,6]}, 5.34, atom].

[1,2,3,{numbers,[4,5,6]},5.34,atom]


Bastante simple, ¿verdad?

2> [97, 98, 99].
"abc"

Los strings son listas y la notación es absolutamente la misma ¿Por qué a la gente no le gusta? Debido a esto:

3> [97,98,99,4,5,6].
[97,98,99,4,5,6]
4> [233].
"é"

Erlang imprimirá listas de números como números solo cuando al menos uno de ellos no pueda representar como una letra. 

Es por eso que quizás hayas escuchado que se dice que Erlang es malo en la manipulación de cadenas: no hay un tipo de cadena incorporado como en la mayoría de los otros lenguajes. Esto se debe a los orígenes de Erlang como lenguaje creado y utilizado por empresas de telecomunicaciones. Nunca (o rara vez) usaron cadenas y, como tal, nunca tuvieron ganas de agregarlas oficialmente. Sin embargo, la mayor parte de la falta de sentido de Erlang en las manipulaciones de cadenas se está solucionando con el tiempo: la máquina virtual ahora admite de forma nativa cadenas Unicode y, en general, se vuelve más rápida en las manipulaciones de cadenas todo el tiempo.

También hay una manera de almacenar cadenas como una estructura de datos binarios, lo que las hace realmente livianas y más rápidas para trabajar. Con todo, todavía faltan algunas funciones en la biblioteca estándar y, si bien el procesamiento de cadenas es definitivamente factible en Erlang, existen lenguajes algo mejores para tareas que necesitan mucho, como Perl o Python.

Para unir listas, usamos el operador ++. Lo opuesto a ++ es -- y eliminará elementos de una lista:

5> [1,2,3] ++ [4,5].
[1,2,3,4,5]
6> [1,2,3,4,5] -- [1,2,3].
[4,5]
7> [2,4,2] -- [2,4].
[2]
8> [2,4,2] -- [2,4,2].
[]

Tanto ++ como -- son asociativos por la derecha. Esto significa que los elementos de muchas operaciones -- o ++ se realizarán de derecha a izquierda, como en los siguientes ejemplos:

9>[1,2,3]-[1,2]-[3].
[3]
10>[1,2,3]-[1,2]-[2].
[2,3]

El primer elemento de una lista se denomina Encabezado y el resto de la lista se denomina Cola. Usaremos dos funciones integradas (BIF) para obtenerlas.

11> hd([1,2,3,4]).
1
12>tl([1,2,3,4]).
[2,3,4]

Las funciones integradas (BIF) suelen ser funciones que no se pueden implementar en Erlang puro y, como tales, se definen en C, o en cualquier lenguaje en el que se implemente Erlang (era Prolog en los años 80). Todavía hay algunos BIF que se pueden realizar en Erlang pero que aún se implementaron en C para proporcionar más velocidad a las operaciones comunes. Un ejemplo de esto es la función length(List), que devolverá la longitud (lo has adivinado) de la lista pasada como argumento.

Como se usa con tanta frecuencia, existe una forma más sencilla de separar el principio del final de una lista con la ayuda de la coincidencia de patrones: [Cabeza|Cola]. Así es como agregarías un nuevo encabezado a una lista:

13> Lista = [2,3,4].
[2,3,4]
14>NuevaLista = [1|Lista].
[1,2,3,4]

Al procesar listas, como normalmente se comienza con la cabeza, podemos desear una forma rápida de almacenar también la cola para operarla más tarde. Si recuerdas la forma en que funcionan las tuplas y cómo usamos la coincidencia de patrones para descomprimir los valores de un punto ({X,Y}), sabrás que podemos cortar el primer elemento (el encabezado) de una lista de manera similar. .

15> [Cabeza|Cola] = NuevaLista.
[1,2,3,4]
16> Cabeza.
1
17> Cola.
[2,3,4]
18> [NuevaCabeza|NuevaCola] = Cola.
[2,3,4]
19> Nuevo jefe.
2

El | que utilizamos se llama operador de contras (constructor). De hecho, cualquier lista se puede construir sólo con el operador:

20> [1 | []].
[1]
21> [2 | [1 | []]].
[2,1]
22> [3 | [2 | [1 | []] ] ].
[3,2,1]

Es decir cualquier lista se puede construir con la siguiente fórmula: [Término1| [Término2 | [... | [TérminoN]]]].... Las listas pueden así definirse recursivamente como un principio que precede a un final, que a su vez es un principio seguido de más principios. En este sentido, podríamos imaginar una lista como una lombriz de tierra: puedes cortarla por la mitad y luego tendrás dos lombrices.

Las formas en que se pueden construir las listas de Erlang a veces resultan confusas para las personas que no están acostumbradas a constructores similares. Para ayudarle a familiarizarse con el concepto, lea todos estos ejemplos (pista: todos son equivalentes):

[a B C D]
[a, b, c, d | []]
[a, b | [cd]]
[a, b | [c | [d]]]
[un | [b | [c | [d]]]]
[un | [b | [c | [d | [] ]]]]

Una vez entendido esto, debería poder lidiar con listas por comprensión.

Usando el formulario [1 | 2] ofrece lo que llamamos una "lista inadecuada". Las listas inadecuadas funcionarán cuando coincida el patrón en la forma [Cabeza|Cola], pero no podrán usarse con funciones estándar de Erlang (longitud uniforme()). Esto se debe a que Erlang espera listas adecuadas. Las listas adecuadas terminan con una lista vacía como última celda. Al declarar un elemento como [2], la lista se forma automáticamente de manera adecuada. ¡Como tal, [1|[2]] funcionaría! Las listas inadecuadas, aunque sintácticamente válidas, tienen un uso muy limitado fuera de las estructuras de datos definidas por el usuario.

domingo, 18 de febrero de 2024

Quickysort en Erlang


Un Algoritmo que me gusta mucho es el quicksort, porque es un algoritmo por demás claro. Ya he escrito lo fácil que es implementarlo en Rust, haskell y lisp

Ahora le toca a Erlang. Básicamente el algoritmo toma un pivot y agrupa los menores del pivot al principio y los mayores al final y aplica quicksort a estos 2 grupos. Y si la lista es vacia o tiene un elemento, ya esta ordenada. 

Vamos al código: 


sort([Pivot|T]) ->

    sort([ X || X <- T, X < Pivot]) ++

    [Pivot] ++

    sort([ X || X <- T, X >= Pivot]);

sort([]) -> [].


Y listo!!

viernes, 16 de febrero de 2024

Chequeando "TODOs" con Roslyn


Vamos a buscar // TODO: ... con Roslyn 

Para empezar en una aplicación de consola simple agreguemos algunos paquetes. Microsoft.CodeAnalysis.CSharp y Microsoft.CodeAnalysis.CSharp.Workspaces son lo que necesitamos.

Y hacemos esto: 


const int ExitOK = 0;

const int ExitError = 99;

const int ExitIssueFound = 1;


static async Task<int> MainAsync(string[] args)

{

var workspace = await GetWorkspace().ConfigureAwait(false);

if (workspace == null)

return ExitError;

using (workspace)

{

var issueFound = false;

foreach (var project in workspace.CurrentSolution.Projects)

{

foreach (var document in project.Documents)

{

var documentWritten = false;

var root = await document.GetSyntaxRootAsync().ConfigureAwait(false);

foreach (var item in root.DescendantTrivia().Where(x => x.IsKind(SyntaxKind.SingleLineCommentTrivia)))

{

var match = Regex.Match(item.ToFullString(), @"//\s?TODO:\s*(.*)");

if (match.Success)

{

issueFound = true;

var text = match.Groups[1].Value;

if (!documentWritten)

{

documentWritten = true;

Console.WriteLine(MinimizePath(document.FilePath));

}

var position = item.GetLocation().GetMappedLineSpan();

var line = position.StartLinePosition.Line;

Console.WriteLine($"\tL{line}:\t{text}");

}

}

}

}

return issueFound ? ExitIssueFound : ExitOK;

}

}


static async Task<Workspace> GetWorkspace()

{

var workspace = MSBuildWorkspace.Create();

var solution = Directory.EnumerateFiles(Environment.CurrentDirectory, "*.sln", SearchOption.TopDirectoryOnly).FirstOrDefault();

if (solution != null)

{

await workspace.OpenSolutionAsync(solution).ConfigureAwait(false);

return workspace;

}

var project = Directory.EnumerateFiles(Environment.CurrentDirectory, "*.csproj", SearchOption.TopDirectoryOnly).FirstOrDefault();

if (project != null)

{

await workspace.OpenProjectAsync(project).ConfigureAwait(false);

return workspace;

}

return null;

}


static string MinimizePath(string path)

{

return path.Remove(0, Environment.CurrentDirectory.Length + 1);

}


miércoles, 14 de febrero de 2024

Tuplas en Erlang


Una tupla es una forma de organizar datos. Es una forma de agrupar muchos términos cuando sabes cuántos hay. En Erlang, una tupla se escribe en la forma {Elemento1, Elemento2,..., ElementoN}. Como ejemplo, me darías las coordenadas (x,y) si quisieras decirme la posición de un punto en una gráfica cartesiana. Podemos representar este punto como una tupla de dos términos:

1> X = 10, Y = 4.

4

2> Point = {X,Y}.

{10,4}


En este caso, un punto siempre serán dos términos. En lugar de llevar las variables X e Y por todos lados, solo tienes que llevar una. Sin embargo, ¿qué puedo hacer si recibo un punto y sólo quiero la coordenada X? No es difícil extraer esa información. Recuerde que cuando asignamos valores, Erlang nunca se quejaría si fueran iguales. 

3> Point = {4,5}.

{4,5}

4> {X,Y} = Point.

{4,5}

5> X.

4

6> {X,_} = Point.

{4,5}


Ahora podemos usar X con el valor. Primero, X e Y no tenían valor y, por lo tanto, se consideraban variables independientes. Cuando los configuramos en la tupla {X,Y} en el lado izquierdo del operador =, el operador = compara ambos valores: {X,Y} vs. {4,5}. Erlang es lo suficientemente inteligente como para descomprimir los valores de la tupla y distribuirlos a las variables independientes en el lado izquierdo. Entonces la comparación es solo {4,5} = {4,5}, ¡lo cual obviamente tiene éxito! Esa es una de las muchas formas de coincidencia de patrones.

En la expresión 6, se utilizo la variable _ anónima y se usa cuando no queremos ese valor. La variable _ siempre se considera independiente y actúa como comodín para la coincidencia de patrones. La coincidencia de patrones para descomprimir tuplas solo funcionará si el número de elementos (la longitud de la tupla) es el mismo.


7> {_,_} = {4,5}.

{4,5}

8> {_,_} = {4,5,6}.

** exception error: no match of right hand side value {4,5,6}


Las tuplas también pueden resultar útiles cuando se trabaja con valores únicos. ¿Cómo es eso? El ejemplo más simple es la temperatura:


9> Temperature = 23.213.

23.213

Bueno, parece un buen día para ir a la playa... Espera, ¿esta temperatura está en Kelvin, Celsius o Fahrenheit?


10> PreciseTemperature = {celsius, 23.213}.

{celsius,23.213}

11> {kelvin, T} = PreciseTemperature.

** exception error: no match of right hand side value {celsius,23.213}


Esto arroja un error, ¡pero es exactamente lo que queremos! Esto es, nuevamente, coincidencia de patrones en acción. El operador = termina comparando {kelvin, T} y {celsius, 23.213}: incluso si la variable T no está unida, Erlang no verá el átomo celsius como idéntico al átomo kelvin al compararlos. Se lanza una excepción que detiene la ejecución del código. Al hacerlo, la parte de nuestro programa que espera una temperatura en Kelvin no podrá procesar las temperaturas enviadas en Celsius. Esto hace que sea más fácil para el programador saber qué se envía y también funciona como ayuda de depuración. Una tupla que contiene un átomo seguido de un elemento se denomina "tupla etiquetada". Cualquier elemento de una tupla puede ser de cualquier tipo, incluso de otra tupla:


12> {point, {X,Y}}.

{point,{4,5}}


martes, 13 de febrero de 2024

Quicksort en Rust


Un Algoritmo que me gusta mucho es el quicksort, porque es un algoritmo por demás claro. Ya he escrito lo fácil que es implementarlo en haskell y lisp

Ahora le toca a Rust. Básicamente el algoritmo toma un pivot y agrupa los menores del pivot al principio y los mayores al final y aplica quicksort a estos 2 grupos. Y si la lista es vacia o tiene un elemento, ya esta ordenada. 

Vamos al código: 

fn quick_sort<T: Ord>(mut arr: Vec<T>) -> Vec<T> {

    if arr.len() <= 1 {

        return arr;

    }


    let pivot = arr.remove(0);

    let mut left = vec![];

    let mut right = vec![];


    arr.into_iter().for_each(|item| {

        if item <= pivot {

            left.push(item);

        } else {

            right.push(item);

        }

    });


    let mut sorted_left = quick_sort(left);


    sorted_left.push(pivot);

    sorted_left.append(&mut quick_sort(right));


    sorted_left

}


Y lo probamos en el main: 


fn main() {

    let arr = vec![10, 80, 30, 90, 40, 50, 70];


    println!("{:?}", arr);

    println!("{:?}", quick_sort(arr));

}

viernes, 9 de febrero de 2024

Álgebra booleana y operadores de comparación en Erlang


Uno estaría en serios problemas si no pudiera distinguir entre lo que es pequeño y lo grande, lo que es verdadero y lo falso. Como cualquier otro lenguaje, Erlang tiene formas que le permiten utilizar operaciones booleanas y comparar elementos.

El álgebra booleana es muy simple:


1> true and false.

false

2> false or true.

true

3> true xor false.

true

4> not false.

true

5> not (true and true).

false

Los operadores booleanos and y or siempre evaluarán los argumentos en ambos lados del operador. Si desea tener operadores de cortocircuito (que solo evaluarán el argumento del lado derecho si es necesario), use andalso y orelse.

La prueba de igualdad o desigualdad también es muy simple, pero tiene símbolos ligeramente diferentes a los que se ven en muchos otros lenguajes:


6> 5 =:= 5.

true

7> 1 =:= 0.

false

8> 1 =/= 0.

true

9> 5 =:= 5.0.

false

10> 5 == 5.0.

true

11> 5 /= 5.0.

false


En primer lugar, si su lenguaje habitual usa == y != para probar a favor y en contra de la igualdad, Erlang usa =:= y =/=. Las tres últimas expresiones (líneas 9 a 11) también nos presentan un problema: a Erlang no le importarán los números flotantes y enteros en aritmética, pero sí lo hará al compararlos. Pero no te preocupes, porque los operadores == y /= están ahí para ayudarte en estos casos. Es importante recordar esto si desea igualdad exacta o no.

Otros operadores para comparaciones son < (menor que), > (mayor que), >= (mayor o igual que) y =< (menor o igual que). Este último está al revés (en mi opinión) y es la fuente de muchos errores de sintaxis en mi código. Esté atento a eso =<.


12> 1 < 2.

true

13> 1 < 1.

false

14> 1 >= 1.

true

15> 1 =< 1.

true


¿Qué pasa al hacer 5 + llama o 5 == verdadero? ¡No hay mejor manera de saberlo que probarlo y luego asustarse con mensajes de error!


12> 5 + llama.

** exception error: bad argument in an arithmetic expression

in operator  +/2 called as 5 + llama


¡Bien! ¡A Erlang realmente no le gusta que hagas mal uso de algunos de sus tipos fundamentales! El emulador devuelve un bonito mensaje de error aquí. ¡Nos dice que no le gusta uno de los dos argumentos utilizados en torno al operador +!

Sin embargo, algunas veces no se toma tan en serio el tema de los tipos :


13> 5 =:= true.

false


¿Por qué rechaza distintos tipos en unas operaciones pero no en otras? Si bien Erlang no te permite agregar nada con todo, te permitirá compararlos. Esto se debe a que los creadores de Erlang pensaron que el pragmaticismo vence a la teoría y decidieron que sería fantástico poder escribir simplemente cosas como algoritmos de clasificación generales que pudieran ordenar cualquier término. Está ahí para simplificarle la vida y puede hacerlo la gran mayoría del tiempo.

Hay una última cosa a tener en cuenta al hacer álgebra booleana y comparaciones:


14> 0 == false.

false

15> 1 < false.

true


Lo más probable es que te estés tirando de los pelos si vienes de lenguajes procedimentales o de la mayoría de los lenguajes orientados a objetos. ¡La línea 14 debe evaluarse como verdadera y la línea 15 como falsa! Después de todo, falso significa 0 y verdadero es cualquier otra cosa. Excepto en Erlang. 

Erlang no tiene valores booleanos verdadero y falso. Los términos verdadero y falso son átomos, pero están lo suficientemente bien integrados en el lenguaje como para no tener problemas con eso, siempre y cuando no esperes que falso y verdadero signifiquen otra cosa que falso y verdadero.

Nota: El orden correcto de cada elemento en una comparación es el siguiente:

number < atom < reference < fun < port < pid < tuple < list < bit string

¡Solo recuerda que es por eso que puedes comparar cualquier cosa con cualquier cosa! Para citar a Joe Armstrong, uno de los creadores de Erlang: "El orden real no es importante, pero sí es importante que un orden total esté bien definido".

martes, 6 de febrero de 2024

Atoms en Erlang


Hay una razón por la cual los nombres de las variables no pueden comenzar en minuscula: los átomos. Los átomos son literales, constantes con su propio nombre para el valor. Lo que ves es lo que obtienes y no esperes más. El átomo gato significa "gato" y listo. No puedes jugar con ello, no puedes cambiarlo, no puedes hacerlo pedazos; es gato. Y listo!

Si bien las palabras individuales que comienzan con una letra minúscula son una forma de escribir un átomo, hay más de una manera de hacerlo:

1> atom.

atom

2> atoms_rule.

atoms_rule

3> atoms_rule@erlang.

atoms_rule@erlang

4> 'Atoms can be cheated!'.

'Atoms can be cheated!'

5> atom = 'atom'.

atom


Un átomo debe estar entre comillas simples (') si no comienza con una letra minúscula o si contiene otros caracteres además de alfanuméricos, guión bajo (_) o @.

La expresión 5 también muestra que un átomo con comillas simples es exactamente igual que un átomo similar sin ellas.

Los átomos  permiten olvidar de los valores subyacentes: por ejemplo los colores de los ojos pueden ser simplemente "azules", "marrones", "verdes" y "otros". No es necesario enumerarlos o que tengan otro valor. Estos colores se pueden usar en cualquier parte de cualquier código: los valores subyacentes nunca chocarán y es imposible que una constante de este tipo no esté definida. Si realmente necesitamos constantes con valores asociados, hay una manera de hacerlo que veremos más adelante.

Por lo tanto, un átomo es principalmente útil para expresar o calificar datos acoplados a él. Usado solo, es un poco más difícil encontrarle un buen uso. Por eso no pasaremos más tiempo jugando con ellos; su mejor uso se producirá cuando se combinen con otros tipos de datos.

Los átomos son realmente agradables y una excelente manera de enviar mensajes o representar constantes. Sin embargo, existen riesgos al usar átomos para demasiadas cosas: se hace referencia a un átomo en una "tabla de átomos" que consume memoria (4 bytes/átomo en un sistema de 32 bits, 8 bytes/átomo en un sistema de 64 bits). La tabla de átomos no se recolecta como basura, por lo que los átomos se acumularán hasta que el sistema se vuelque, ya sea por el uso de la memoria o porque se declararon 1048577 átomos.

Esto significa que los átomos no deberían generarse dinámicamente por ningún motivo; Si su sistema tiene que ser confiable y la entrada del usuario permite que alguien lo bloquee a voluntad diciéndole que cree átomos, está en serios problemas. Los átomos deben verse como herramientas para el desarrollador porque, sinceramente, es lo que son.

Algunos átomos son palabras reservadas y no se pueden usar excepto para lo que los diseñadores del lenguaje querían que fueran: nombres de funciones, operadores, expresiones, etc. Estos son: after and andalso band begin bnot bor bsl bsr bxor case catch cond div end fun if let not of or orelse query receive rem try when xor

lunes, 5 de febrero de 2024

Threads en Rust


Los subprocesos de Rust funcionan de manera similar a otros lenguajes:

use std::thread;

use std::time::Duration;


fn main() {

    thread::spawn(|| {

        for i in 1..10 {

            println!("Count in thread: {i}!");

            thread::sleep(Duration::from_millis(5));

        }

    });


    for i in 1..5 {

        println!("Main thread: {i}");

        thread::sleep(Duration::from_millis(5));

    }

}


  • Los subprocesos son todos subprocesos de demonio, el subproceso principal no los espera.
  • Los pánicos en los hilos son independientes entre sí.
  • Los pánicos pueden llevar una carga útil, que se puede descomprimir con downcast_ref.


Invariables en Erlang


Hacer aritmética está bien, pero no llegarás muy lejos sin poder almacenar los resultados en algún lugar. Para eso, usaremos variables. Las variables no pueden variar en la programación funcional. El comportamiento básico de las variables se puede demostrar con estas 7 expresiones (las variables comienzan con una letra mayúscula):

1> One.

* 1: variable 'One' is unbound

2> One = 1.

1

3> Un = Uno = One = 1.

1

4> Two = One + One.

2

5> Two = 2.        

2

6> Two = Two + 1.

** exception error: no match of right hand side value 3

7> two = 2.

** exception error: no match of right hand side value 2


Lo primero que nos dicen estos comandos es que puedes asignar un valor a una variable exactamente una vez; entonces puedes 'fingir' asignar un valor a una variable si es el mismo valor que ya tiene. Si es diferente, Erlang se quejará. Es una observación correcta, pero la explicación es un poco más compleja y depende del operador =. El operador = (no las variables) tiene la función de comparar valores y quejarse si son diferentes. Si son iguales, devuelve el valor:


8> 47 = 45 + 2.

47

9> 47 = 45 + 3.

** exception error: no match of right hand side value 48

Lo que hace este operador cuando se mezcla con variables es que si el término del lado izquierdo es una variable y no está vinculado (no tiene ningún valor asociado), Erlang vinculará automáticamente el valor del lado derecho a la variable de la izquierda. lado. En consecuencia, la comparación tendrá éxito y la variable mantendrá el valor en la memoria.

Este comportamiento del operador = es la base de algo llamado 'coincidencia de patrones', que tienen muchos lenguajes de programación funcionales, aunque la forma de hacer las cosas de Erlang suele considerarse más flexible y completa que las alternativas. 

La otra cosa que nos dijeron los comandos 1-7 es que los nombres de las variables deben comenzar con una letra mayúscula. El comando 7 falló porque la palabra dos tenía una letra minúscula al principio. Técnicamente, las variables también pueden comenzar con un guión bajo ('_'), pero por convención su uso está restringido a valores que no te interesan, pero sentiste que era necesario documentar lo que contienen.

También puedes tener variables que sean solo un guión bajo:

10> _ = 14+3.

17

11> _.

* 1: variable '_' is unbound


A diferencia de cualquier otro tipo de variable, nunca almacenará ningún valor. Totalmente inútil por ahora, pero sabrás que existe cuando lo necesitemos.

Nota: Si está probando en el shell y guarda el valor incorrecto en una variable, es posible "borrar" esa variable usando la función f(Variable). Si desea borrar todos los nombres de variables, haga f( )..

Estas funciones están ahí sólo para ayudarle durante las pruebas y sólo funcionan en el shell. Al escribir programas reales, no podremos destruir valores de esa manera. Poder hacerlo solo en el shell tiene sentido si se reconoce que Erlang es utilizable en escenarios industriales: es completamente posible tener un shell activo durante años sin interrupción... Apostemos a que la variable X se usaría más de una vez. en ese período de tiempo.



Números en Erlang

 


En el shell de Erlang, las expresiones deben terminar con un punto seguido de un espacio en blanco (salto de línea, un espacio, etc.); de lo contrario, no se ejecutarán. Puedes separar expresiones con comas, pero solo se mostrará el resultado de la última (las demás aún se ejecutan). Esta es ciertamente una sintaxis inusual para la mayoría de las personas y proviene de los días en que Erlang se implementó directamente en Prolog, un lenguaje de programación lógica.


Abramos el shell Erlang y escribamos:


1> 2 + 15.

17

2> 49 * 100.

4900

3> 1892 - 1472.

420

4> 5 / 2.

2.5

5> 5 div 2.

2

6> 5 rem 2.

1


Deberías haber notado que a Erlang no le importa si ingresas números de punto flotante o enteros: ambos tipos son compatibles cuando se trata de aritmética. Una calculadora con el número '80085' escrito. Los números enteros y los valores flotantes son prácticamente los únicos tipos de datos que los operadores matemáticos de Erlang manejarán de forma transparente. Sin embargo, si desea tener la división de entero a entero, usemos div, y para tener el operador de módulo, usemos rem (resto).


Podemos utilizar varios operadores en una sola expresión y las operaciones matemáticas obedecen a las reglas de precedencia normales.


7> (50 * 100) - 4999.

1

8> -(50 * 100 - 4999).

-1

9> -50 * (100 - 4999).

244950


Si deseamos expresar números enteros en bases distintas a la base 10, simplemente ingresamos el número como Base#Valor (dada que la Base está en el rango 2..36):


10> 2#101010.

42

11> 8#0677.

447

12> 16#AE.

174



sábado, 3 de febrero de 2024

Printing en Go


La impresión formateada en Go utiliza un estilo similar a la familia printf de C, pero es más rica y más general. Las funciones se encuentran en el paquete fmt y tienen nombres en mayúscula: fmt.Printf, fmt.Fprintf, fmt.Sprintf, etc. Las funciones de cadena (Sprintf, etc.) devuelven una cadena en lugar de enviar la cadena a un buffer determinado.

No es necesario proporcionar un formato. Para cada uno de Printf, Fprintf y Sprintf hay otro par de funciones, por ejemplo Print y Println. Estas funciones no toman una cadena de formato sino que generan un formato predeterminado para cada argumento. Las versiones Println también insertan un espacio en blanco entre los argumentos y agregan una nueva línea a la salida, mientras que las versiones Print agregan espacios en blanco solo si el operando en ninguno de los lados es una cadena. En este ejemplo, cada línea produce el mismo resultado.


fmt.Printf("Hello %d\n", 23)

fmt.Fprint(os.Stdout, "Hello ", 23, "\n")

fmt.Println("Hello", 23)

fmt.Println(fmt.Sprint("Hello ", 23))


Las funciones de impresión formateadas fmt.Fprint y las demás toman como primer argumento cualquier objeto que implemente la interfaz io.Writer; por ejemplo os.Stdout y os.Stderr.

Aquí las cosas empiezan a diferir de C. Primero, los formatos numéricos como %d no aceptan indicadores de signo o tamaño; en cambio, las rutinas de impresión utilizan el tipo de argumento para decidir estas propiedades.


var x uint64 = 1<<64 - 1

fmt.Printf("%d %x; %d %x\n", x, x, int64(x), int64(x))


Esto imprime: 


18446744073709551615 ffffffffffffffff; -1 -1


Si solo desea la conversión predeterminada, como decimal para números enteros, puede usar el formato general %v (para “valor”); el resultado es exactamente lo que producirían Print y Println. Además, ese formato puede imprimir cualquier valor, incluso matrices, sectores, estructuras y mapas. Aquí hay una declaración impresa para el mapa de zona horaria.


fmt.Printf("%v\n", timeZone)  // or just fmt.Println(timeZone)


lo que da salida:


map[CST:-21600 EST:-18000 MST:-25200 PST:-28800 UTC:0]


Para los mapas, Printf y las demás clasifican la salida lexicográficamente por clave.


Al imprimir una estructura, el formato modificado %+v anota los campos de la estructura con sus nombres y, para cualquier valor, el formato alternativo %#v imprime el valor en la sintaxis Go completa.


type T struct {

    a int

    b float64

    c string

}

t := &T{ 7, -2.35, "abc\tdef" }

fmt.Printf("%v\n", t)

fmt.Printf("%+v\n", t)

fmt.Printf("%#v\n", t)

fmt.Printf("%#v\n", timeZone)


imprime : 



&{7 -2.35 abc   def}

&{a:7 b:-2.35 c:abc     def}

&main.T{a:7, b:-2.35, c:"abc\tdef"}

map[string]int{"CST":-21600, "EST":-18000, "MST":-25200, "PST":-28800, "UTC":0}


(Tenga en cuenta los símbolos). Ese formato de cadena entre comillas también está disponible a través de %q cuando se aplica a un valor de tipo cadena o []byte. El formato alternativo %#q utilizará comillas inversas si es posible. (El formato %q también se aplica a números enteros y runas, lo que produce una constante rúnica entre comillas simples). Además, %x funciona en cadenas, matrices de bytes y porciones de bytes, así como en números enteros, generando una cadena hexadecimal larga y con un espacio. en el formato (% x) pone espacios entre los bytes.


Otro formato útil es %T, que imprime el tipo de un valor.


fmt.Printf("%T\n", timeZone)


imprime: 


map[string]int


Si desea controlar el formato predeterminado para un tipo personalizado, todo lo que se requiere es definir un método con la cadena de firma String() en el tipo. Para nuestro tipo T simple, podría verse así.


func (t *T) String() string {

    return fmt.Sprintf("%d/%g/%q", t.a, t.b, t.c)

}

fmt.Printf("%v\n", t)


para imprimir en el formato


7/-2.35/"abc\tdef"


(Si necesita imprimir valores de tipo T así como punteros a T, el receptor de String debe ser de tipo valor; este ejemplo usó un puntero porque es más eficiente e idiomático para tipos de estructuras).

Nuestro método String puede llamar a Sprintf porque las rutinas de impresión son completamente reentrantes y se pueden empaquetar de esta manera. Sin embargo, hay un detalle importante que debe comprender acerca de este enfoque: no construya un método String llamando a Sprintf de una manera que se repetirá en su método String indefinidamente. Esto puede suceder si la llamada de Sprintf intenta imprimir el receptor directamente como una cadena, lo que a su vez invocará el método nuevamente. Es un error común y fácil de cometer, como muestra este ejemplo.


type MyString string


func (m MyString) String() string {

    return fmt.Sprintf("MyString=%s", m) // Error: will recur forever.

}


También es fácil de solucionar: convierta el argumento al tipo de cadena básica, que no tiene el método.


type MyString string

func (m MyString) String() string {

    return fmt.Sprintf("MyString=%s", string(m)) // OK: note conversion.

}


Otra técnica de impresión consiste en pasar los argumentos de una rutina de impresión directamente a otra rutina similar. La firma de Printf usa el tipo ...interfaz{} como argumento final para especificar que un número arbitrario de parámetros (de tipo arbitrario) puede aparecer después del formato.


func Printf(format string, v ...interface{}) (n int, err error) {


Dentro de la función Printf, v actúa como una variable de tipo []interfaz{} pero si se pasa a otra función variable, actúa como una lista normal de argumentos. Aquí está la implementación de la función log.Println que usamos anteriormente. Pasa sus argumentos directamente a fmt.Sprintln para el formato real.


// Println prints to the standard logger in the manner of fmt.Println.

func Println(v ...interface{}) {

    std.Output(2, fmt.Sprintln(v...))  // Output takes parameters (int, string)

}


Escribimos ... después de v en la llamada anidada a Sprintln para decirle al compilador que trate a v como una lista de argumentos; de lo contrario, simplemente pasaría v como argumento de un solo segmento.

Erlang shell




En Erlang, puedes probar la mayoría de tus cosas en un emulador; ejecutará sus scripts cuando los compile e implemente, pero también te permite ejecutar código en vivo. Para esto, iniciamos el shell en Linux y luego escribimos $ erl. Y si esta todo bien, deberíamos ver un texto como este:

Erlang R13B01 (erts-5.7.2) [source] [smp:2:2] [rq:2] [async-threads:0] [hipe] [kernel-poll:false]

Eshell V5.7.2  (abort with ^G)


Para los usuarios de Windows, aún pueden ejecutar el shell erl.exe, pero se recomienda que se utilice werl.exe, que se puede encontrar en el menú de inicio (programas > Erlang). Werl es una implementación solo para Windows del shell Erlang, que tiene su propia ventana con barras de desplazamiento y admite edición de línea de comandos (como copiar y pegar, lo que llegó a ser una molestia con el shell cmd.exe estándar en Windows). El shell erl aún es necesario si desea redirigir la entrada o salida estándar, o utilizar canalizaciones.

Podremos ingresar y ejecutar código en el emulador, pero primero, veamos cómo podemos movernos en él. El shell Erlang tiene un editor de líneas incorporado basado en un subconjunto de Emacs, un popular editor de texto que se utiliza desde los años 70. Si conoce Emacs, debería estar bien. Para los demás, te irá bien de todos modos.

En primer lugar, si escribe algo de texto y luego va ^A (Ctrl+A), debería ver que el cursor se mueve al principio de la línea. ^E (Ctrl+E) te lleva al final. Puede usar las teclas de flecha para avanzar, retroceder, mostrar líneas anteriores o siguientes para poder repetir el código.

Si escribe algo como li y luego presiona "tab", el shell habrá completado los términos y si presionamos tabulador nuevamente y el shell le sugerirá muchas funciones para usar después. Este es Erlang completando las listas de módulos y luego sugiriendo funciones a partir de ellos. 

Creo que hemos visto suficiente funcionalidad del shell para estar bien, excepto por una cosa: ¡no sabemos cómo salir! Hay una forma rápida de descubrir cómo hacerlo. Simplemente escriba help(). y debería obtener información sobre un montón de comandos que puede usar en el shell (no olvide el punto (.) ya que es necesario para que se ejecute el comando). Usaremos algunos de ellos más adelante, pero la única línea que nos preocupa para poder salir es

q() -- salir - abreviatura de init:stop()

Esta es una manera de hacerlo (de hecho, dos maneras). Si estabas prestando atención, cuando inició el shell, hubo un comentario sobre "abortar con ^G". ¡Hagámoslo y luego presione h para obtener ayuda!


User switch command

--> h

c [nn]            - connect to job

i [nn]            - interrupt job

k [nn]            - kill job

j                 - list all jobs

s [shell]         - start local shell

r [node [shell]]  - start remote shell

q        - quit erlang

? | h             - this message

-->


Si escribe i y luego c, Erlang debería detener el código que se está ejecutando actualmente y devolverlo a un shell responsivo. j le dará una lista de procesos en ejecución (una estrella después de un número indica que este es el trabajo que está ejecutando actualmente), que luego puede interrumpir con i seguido del número. Si usa k, matará el shell tal como está en lugar de simplemente interrumpirlo. Presione s para iniciar uno nuevo.


Eshell V5.7.2  (abort with ^G)

1> "OH NO THIS SHELL IS UNRESPONSIVE!!! *hits ctrl+G*"

User switch command

--> k

--> c

Unknown job

--> s

--> j

2* {shell,start,[]}

--> c 2

Eshell V5.7.2  (abort with ^G)

1> "YESS!"


Si vuelve a leer el texto de ayuda, notará que podemos iniciar shells remotos. 

gRPC Client


De manera similar al lado del servidor, podemos generar el código del lado del cliente utilizando la definición del servicio. El código del cliente proporciona los mismos métodos que el servidor, que su código de cliente puede invocar; el código del cliente los traduce en llamadas de red de invocación de funciones remotas que van al lado del servidor. Dado que las definiciones de servicios gRPC son independientes del lenguaje, puede generar clientes y servidores para cualquier lenguaje admitido (a través de implementaciones de terceros) de su elección. 

Entonces, veamos un ejemplo en Java. A pesar del lenguaje de programación que utilizamos, los pasos simples involucrados en una implementación del lado del cliente implican configurar una conexión con el servidor remoto, adjuntar el código auxiliar del cliente a esa conexión e invocar el método remoto usando el código auxiliar del cliente.


ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 8080)

.usePlaintext(true)

 .build(); 


// Initialize blocking stub using the channel 

ProductInfoGrpc.ProductInfoBlockingStub stub = ProductInfoGrpc.newBlockingStub(channel); 


// Call remote method using the blocking stub 

StringValue productID = stub.addProduct( 

     Product.newBuilder() 

            .setName("Apple iPhone 11") 

            .setDescription("Meet Apple iPhone 11." + "All-new dual-camera system with " + "Ultra Wide and Night mode.") 

   .build());