Translate

sábado, 4 de mayo de 2024

¿Cuándo deberíamos usar funciones, if o case... of en Erlang ?


Cuál usar es bastante difícil de responder. La diferencia entre llamadas a funciones y case... of es mínima: de hecho, se representan de la misma manera a bajo nivel, y usar una u otra de manera efectiva tiene el mismo costo en términos de rendimiento. Una diferencia entre ambos es cuando es necesario evaluar más de un argumento: función (A, B) -> ... fin. puede tener guards y valores para comparar con A y B, pero una expresión de caso debería formularse de manera similar a:

case {A,B} of

Pattern Guards -> ...

end.


Esta forma rara vez se ve y puede sorprender un poco al lector. En situaciones similares, utilizar una llamada a función podría ser más apropiado. Por otro lado, la función insert/2 que habíamos escrito anteriormente es posiblemente más limpia tal como está en lugar de tener una llamada de función inmediata para rastrear una simple cláusula verdadera o falsa.

Entonces, la otra pregunta es ¿por qué usarías if, dados los casos y las funciones son lo suficientemente flexibles como para abarcarlos incluso a través de guards? La razón detrás de if es bastante simple: se agregó al lenguaje como una forma breve de tener guards sin necesidad de escribir toda la parte de pattern matching cuando no era necesario.

Por supuesto, todo esto tiene que ver más con las preferencias personales y con lo que puedes encontrar con más frecuencia. No hay una buena respuesta sólida. La comunidad de Erlang todavía debate. 

miércoles, 1 de mayo de 2024

Depreciaciones o marcar funciones como viejas en Gleam


pub fn main() {

  old_function()

  new_function()

}


@deprecated("Use new_function instead")

fn old_function() {

  Nil

}


fn new_function() {

  Nil

}


y la salida sería: 


warning: Deprecated value used

  ┌─ /src/main.gleam:2:3

  │

2 │   old_function()

  │   ^^^^^^^^^^^^ This value has been deprecated


Las funciones y otras definiciones se pueden marcar como obsoletas utilizando el atributo @deprecated.

Si se hace referencia a una función obsoleta, el compilador emitirá una advertencia, informándole al programador que debe actualizar su código.

El atributo de obsolescencia recibe un mensaje y este se mostrará al usuario en la advertencia. En el mensaje, se explica al usuario el nuevo enfoque o la función de reemplazo, o diríjalo a la documentación sobre cómo actualizar.

Case ... of de Erlang


Si la expresión if es como una Guards, Case ... of  es como el encabezado de la función completa: puedes tener la coincidencia de patrones complejos que puedes usar con cada argumento, ¡y puedes tener Guards!

Escribiremos la función de agregar para conjuntos (una colección de valores únicos) que representaremos como una lista desordenada:


insert(X,[]) ->

    [X];

insert(X,Set) ->

    case lists:member(X,Set) of

        true  -> Set;

        false -> [X|Set]

    end.


Si enviamos un conjunto vacío y un término X para agregar, nos devuelve una lista que contiene solo X. De lo contrario, la función de listas: member/2 verifica si un elemento es parte de una lista y devuelve verdadero si es, falso si no lo es. En el caso de que ya tuviéramos el elemento X en el conjunto, no necesitamos modificar la lista. De lo contrario, agregamos X como primer elemento de la lista.

En este caso, la combinación de patrones fue realmente sencilla. Puede volverse más complejo:


beach(Temperature) ->

    case Temperature of

        {celsius, N} when N >= 20, N =< 45 ->

            'favorable';

        {kelvin, N} when N >= 293, N =< 318 ->

            'scientifically favorable';

        {fahrenheit, N} when N >= 68, N =< 113 ->

            'favorable in the US';

        _ ->

            'avoid beach'

    end.


Aquí, la respuesta de "¿es el momento adecuado para ir a la playa?" se da en 3 sistemas de temperatura diferentes: grados Celsius, Kelvin y Fahrenheit. El pattern matching y guards se combinan para devolver una respuesta que satisfaga todos los usos. Como se señaló anteriormente, case... of expresiones son más o menos lo mismo que un grupo de cabezales funcionales con guards. De hecho podríamos haber escrito nuestro código de la siguiente manera:


beachf({celsius, N}) when N >= 20, N =< 45 ->

    'favorable';

...

beachf(_) ->

    'avoid beach'.


Esto plantea la pregunta: ¿cuándo deberíamos usar funciones if o case... of ?

Comentarios de documentación




//// A module containing some unusual functions and types.


/// A type where the value can never be constructed.

/// Can you work out why?

pub type Never {

  Never(Never)

}


/// Call a function twice with an initial value.

///

pub fn twice(argument: value, my_function: fn(value) -> value) -> value {

  my_function(my_function(argument))

}


/// Call a function three times with an initial value.

///

pub fn thrice(argument: value, my_function: fn(value) -> value) -> value {

  my_function(my_function(my_function(argument)))

}


La documentación y los comentarios son herramientas importantes para hacer que sea más fácil trabajar y comprender el código.

Además de los // comentarios regulares, Gleam tiene /// y //// comentarios que se utilizan para adjuntar documentación al código.

/// se utiliza para documentar tipos y funciones, y debe colocarse inmediatamente antes del tipo o función que se está documentando.

//// se utiliza para documentar módulos y debe colocarse en la parte superior del módulo.

If de Erlang


Los if actúan como Guards y comparten la sintaxis, pero en el cuerpo de la función. De hecho, las cláusulas if se llaman patrones de Guards. Para ver qué tan similar a las Guards es la expresión if, miremos los siguientes ejemplos:


-module(what_the_if).

-export([heh_fine/0]).


heh_fine() ->

    if 1 =:= 1 ->

        works

    end,

    if 1 =:= 2; 1 =:= 1 ->

        works

    end,

    if 1 =:= 2, 1 =:= 1 ->

        fails

    end.


Guarde esto como what_the_if.erl y probémoslo:


1> c(what_the_if).

./what_the_if.erl:12: Warning: no clause will ever match

./what_the_if.erl:12: Warning: the guard for this clause evaluates to 'false'

{ok,what_the_if}

2> what_the_if:heh_fine().

** exception error: no true branch found when evaluating an if expression

     in function  what_the_if:heh_fine/0


¡UH oh! el compilador nos advierte que ninguna cláusula del if en la línea 12 (1 =:= 2, 1 =:= 1) coincidirá porque su única protección se evalúa como falsa. Recuerde, en Erlang todo tiene que devolver algo, y las expresiones if no son una excepción a la regla. Como tal, cuando Erlang no puede encontrar una manera de que un Guards tenga éxito, se bloqueará: no puede no devolver algo. Como tal, necesitamos agregar una rama general que siempre tendrá éxito pase lo que pase. En la mayoría de los lenguajes, esto se llamaría "else". En Erlang, usamos 'true' dado que si una condición no es verdadera, true siempre lo será:


oh_god(N) ->

    if N =:= 2 -> might_succeed;

       true -> always_does  %% this is Erlang's if's 'else!'

    end.


Y ahora si probamos esta nueva función (la antigua seguirá escupiendo advertencias, las ignorará o las tomará como recordatorio de lo que no se debe hacer):


3> c(what_the_if).

./what_the_if.erl:12: Warning: no clause will ever match

./what_the_if.erl:12: Warning: the guard for this clause evaluates to 'false'

{ok,what_the_if}

4> what_the_if:oh_god(2).

might_succeed

5> what_the_if:oh_god(3).

always_does


Aquí hay otra función que muestra cómo usar muchos Guards en una expresión if. La función también ilustra cómo cualquier expresión debe devolver algo: Talk tiene el resultado de la expresión if vinculado y luego se concatena en una cadena, dentro de una tupla. Al leer el código, es fácil ver cómo la falta de una rama verdadera arruinaría las cosas, considerando que Erlang no tiene un valor nulo (es decir, NIL de Lisp, NULL de C, None de Python, etc.):


%% note, this one would be better as a pattern match in function heads!

%% I'm doing it this way for the sake of the example.

help_me(Animal) ->

    Talk = if Animal == cat  -> "meow";

              Animal == beef -> "mooo";

              Animal == dog  -> "bark";

              Animal == tree -> "bark";

              true -> "fgdadfgna"

           end,

    {Animal, "says " ++ Talk ++ "!"}.


Y ahora lo probamos:


6> c(what_the_if).

./what_the_if.erl:12: Warning: no clause will ever match

./what_the_if.erl:12: Warning: the guard for this clause evaluates to 'false'

{ok,what_the_if}

7> what_the_if:help_me(dog).

{dog,"says bark!"}

8> what_the_if:help_me("it hurts!").

{"it hurts!","says fgdadfgna!"}


Todo este horror expresado por los nombres de las funciones en what_the_if.erl se expresa con respecto a la construcción del lenguaje if cuando se ve desde la perspectiva del if de cualquier otro lenguaje. En el contexto de Erlang, resulta ser una construcción perfectamente lógica con un nombre confuso.


Argumentos etiquetados en Gleam


import gleam/io


pub fn main() {

  // Without using labels

  io.debug(calculate(1, 2, 3))


  // Using the labels

  io.debug(calculate(1, add: 2, multiply: 3))


  // Using the labels in a different order

  io.debug(calculate(1, multiply: 3, add: 2))

}


fn calculate(value: Int, add addend: Int, multiply multiplier: Int) {

  value * multiplier + addend

}


Cuando las funciones toman varios argumentos, puede resultar difícil recordar cuáles son los argumentos y en qué orden se esperan.

Para ayudar con esto, Gleam admite argumentos etiquetados, donde los argumentos de función reciben una etiqueta externa además de su nombre interno. Estas etiquetas se escriben antes del nombre del argumento en la definición de la función.

Cuando se utilizan argumentos etiquetados, el orden de los argumentos no importa, pero todos los argumentos no etiquetados deben ir antes que los argumentos etiquetados.

No hay costo de rendimiento por el uso de argumentos etiquetados, no asigna un diccionario ni realiza ningún otro trabajo en tiempo de ejecución.

Las etiquetas son opcionales al llamar a una función, corresponde al programador decidir qué es lo más claro en su código.

martes, 30 de abril de 2024

Queres probar código y no queres instalar nada? paiza.io


Queres probar código y no queres instalar nada? 

Bueno podes utilizar paiza. Que es una pagina donde podemos correr código en diferentes lenguajes: 

Bash, C, C#, C++, Clojure, Cobol, CoffeeScript, D, Elixir, Erlang, F#, Go, Haskell, Java, JavaScript, Kotlin, MySQL, Nadesiko, Objective-C, Perl, PHP, Python2, Python3, R, Ruby, Rust, Scala, Scheme, Swift, TypeScript, VB


Dejo link: https://paiza.io/

domingo, 28 de abril de 2024

Pipelines en Gleam


 import gleam/io

import gleam/string


pub fn main() {

  // Without the pipe operator

  io.debug(string.drop_left(string.drop_right("Hello, Joe!", 1), 7))


  // With the pipe operator

  "Hello, Mike!"

  |> string.drop_right(1)

  |> string.drop_left(7)

  |> io.debug


  // Changing order with function capturing

  "1"

  |> string.append("2")

  |> string.append("3", _)

  |> io.debug

}

Es común querer llamar a una serie de funciones, pasando el resultado de una a la siguiente. Con la sintaxis de llamada a función normal, esto puede resultar un poco difícil de leer, ya que hay que leer el código de adentro hacia afuera.

El operador de tubería de Gleam |> ayuda con este problema permitiéndole escribir código de arriba a abajo. Es como el | en consolas unix. Es decir,  toma el resultado de la expresión de su izquierda y lo pasa como argumento a la función de su derecha.

Primero comprobará si el valor de la izquierda podría usarse como primer argumento de la llamada. Por ejemplo, a |> b(1, 2) se convertiría en b(a, 1, 2). De lo contrario, se recurre a llamar al resultado del lado derecho como una función, por ejemplo, b(1, 2)(a)

El código Gleam generalmente se escribe con el "asunto" de la función como primer argumento, para que sea más fácil de canalizar. Si desea canalizar a una posición diferente, puede utilizar una función de captura "_" para insertar el argumento en la posición deseada.

miércoles, 24 de abril de 2024

Correr todos los métodos main de un paquete en C#


Supongamos que tenemos un conjunto de clases que tienen un método main o se puede llamar como quiera y nosotros queremos pasar por parámetro el nombre de la clase y si no pasamos ningun nombre se ejecutan todos los métodos:


 using System;

using System.Linq;

using System.Reflection;



namespace Example

{

    class Program

    {

        static void Main(string[] args)

        {

            if (args[0] != null)

            {

                Run(args[0]);

            }

            else

            {

                var types = AppDomain.CurrentDomain.GetAssemblies()

                    .SelectMany(assembly => assembly.GetTypes())

                    .Where(type => "Example".Equals(type.Namespace)

                                   && !"Program".Equals(type.Name)).OrderBy(type => type.Name);

                foreach (var type in types)

                {

                    Run("Example."+type.Name);

                }

            }

        }


        private static void Run(string className)

        {

            var classType = Type.GetType(className);

            var declaredMethods = (classType as System.Reflection.TypeInfo)?.DeclaredMethods;

            if (declaredMethods == null) return;

            var mainMethod = declaredMethods.FirstOrDefault(method => method.Name == "Main");

            if (mainMethod == null)

            {

                return;

            }

            mainMethod.Invoke(null, null);

        }

    }

}

El método Run corre el metodo main de la clase pasada por parametros. Si no tiene este método, no hace nada. 

Listo!!  

martes, 23 de abril de 2024

Funciones genéricas en Gleam


import gleam/io


pub fn main() {

  let add_one = fn(x) { x + 1 }

  let exclaim = fn(x) { x <> "!" }


  // Invalid, Int and String are not the same type

  // twice(10, exclaim)


  // Here the type variable is replaced by the type Int

  io.debug(twice(10, add_one))


  // Here the type variable is replaced by the type String

  io.debug(twice("Hello", exclaim))

}


// The name `value` refers to the same type multiple times

fn twice(argument: value, my_function: fn(value) -> value) -> value {

  my_function(my_function(argument))

}


Gleam admite genéricos, también conocidos como polimorfismo paramétrico.

Esto funciona utilizando una variable de tipo en lugar de especificar un tipo concreto. Reemplaza cualquier tipo específico que se utilice cuando se llama a la función. Estas variables de tipo se escriben con un nombre en minúscula.

Las variables de tipo no son como cualquier tipo, se reemplazan con un tipo específico cada vez que se llama a la función. 


lunes, 22 de abril de 2024

Usando Roslyn ScriptEngine


La idea es correr C# como un lenguaje script y esto lo podemos hacer con Roslyn. 

Primero, instalamos las dependencias: 


dotnet add package Microsoft.CodeAnalysis.Scripting --version 4.9.2

dotnet add package Microsoft.CodeAnalysis.CSharp.Scripting --version 4.9.2


Y con eso ya estamos, podemos hacer esto: 


using System;

using Microsoft.CodeAnalysis.CSharp.Scripting;

using Microsoft.CodeAnalysis.Scripting;


class Program

{

    static async System.Threading.Tasks.Task Main(string[] args)

    {

        try

        {

            // Creamos el script

            string code = @"

                using System;


                public class MyClass

                {

                    public void MyMethod()

                    {

                        Console.WriteLine(""Hello from C#!"");

                    }

                }";


            // Creamos las opciones (usamos las por defecto) 

            ScriptOptions options = ScriptOptions.Default;


            // Creamos el motor que ejecuta el script

            var script = CSharpScript.Create(code, options);


            // corremos el script

            var result = await script.RunAsync();


            // Y chequeamos si hubo error. 

            if (result.Exception != null)

            {

                Console.WriteLine("Script execution failed: " + result.Exception.Message);

            }

        }

        catch (Exception ex)

        {

            Console.WriteLine("Error executing script: " + ex.Message);

        }

    }

}

Y listo! 

Lo unico malo es que este lenguaje C# no es completo, por ejemplo no se pueden utiliza namespace. Pero para hacer cosas chicas, esta bueno. 

Dejo link: 

domingo, 21 de abril de 2024

Más sobre funciones anónimas en Gleam



import gleam/io


pub fn main() {

  // These two statements are equivalent

  let add_one_v1 = fn(x) { add(1, x) }

  let add_one_v2 = add(1, _)


  io.debug(add_one_v1(10))

  io.debug(add_one_v2(10))

}


fn add(a: Int, b: Int) -> Int {

  a + b

}


Gleam tiene una sintaxis abreviada para crear funciones anónimas que toman un argumento e inmediatamente llaman a otra función con ese argumento: la sintaxis de captura de función.

La función anónima fn(a) { some_function(..., a, ...) } se puede escribir como some_function(..., _, ...), con cualquier número de otros argumentos pasados directamente a la función interna . El guión bajo _ es un marcador de posición para el argumento, equivalente a a en fn(a) { some_function(..., a, ...) }.

Guards en Erlang


Guards son cláusulas adicionales que pueden ir en la cabeza de una función para hacer pattern matching más expresivo. Como se mencionó anteriormente, el pattern matching es algo limitado ya que no puede expresar cosas como un rango de valores o ciertos tipos de datos. 

Por ejemplo, esto con Pattern matching : 

old_enough(0) -> false;

old_enough(1) -> false;

old_enough(2) -> false;

...

old_enough(14) -> false;

old_enough(15) -> false;

old_enough(_) -> true.


Es muy trabajoso pero si utilizamos guards : 


old_enough(X) when X >= 16 -> true;

old_enough(_) -> false.


Tenga en cuenta que Guards deben devolver verdadero para tener éxito. Guards fallará si devuelve falso o si genera una excepción. Supongamos que ahora prohibimos conducir a las personas mayores de 104 años. Nuestras edades válidas para conductores ahora son desde 16 años hasta 104 años. Tenemos que ocuparnos de eso, pero ¿cómo? Agreguemos una segunda cláusula de protección:


right_age(X) when X >= 16, X =< 104 -> true;

right_age(_)  -> false.


La coma (,) actúa de manera similar al operador and y el punto y coma (;) actúa un poco como or. Ambas expresiones deben tener éxito para que retorne verdadero. También podríamos representar la función al revés:


wrong_age(X) when X < 16; X > 104 -> true;

wrong_age(_) -> false.


Con punto y coma, si la primera condición falla, intenta con la segunda y luego con la siguiente, hasta que una tenga éxito o todas fallen.

Un punto negativo de los guards es que no aceptan funciones definidas por el usuario debido a los efectos secundarios. Erlang no es un lenguaje de programación puramente funcional (como lo es Haskell) porque depende mucho de los efectos secundarios: puedes realizar E/S, enviar mensajes entre actores o generar errores como quieras y cuando quieras. No existe una forma trivial de determinar si una función que usaría en una protección imprimiría o no texto o detectaría errores importantes cada vez que se prueba en muchas cláusulas de función. Entonces, en cambio, Erlang simplemente no confía en usted (¡y puede que sea correcto hacerlo!)


lunes, 15 de abril de 2024

Capturas de funciones de Gleam

 


import gleam/io


pub fn main() {

  // These two statements are equivalent

  let add_one_v1 = fn(x) { add(1, x) }

  let add_one_v2 = add(1, _)


  io.debug(add_one_v1(10))

  io.debug(add_one_v2(10))

}


fn add(a: Int, b: Int) -> Int {

  a + b

}


Gleam tiene una sintaxis abreviada para crear funciones anónimas que toman un argumento e inmediatamente llaman a otra función con ese argumento: la sintaxis de captura de función.

La función anónima fn(a) { some_function(..., a, ...) } se puede escribir como some_function(..., _, ...), con cualquier cantidad de otros argumentos pasados directamente a la función interna . El guión bajo _ es un marcador de posición para el argumento, equivalente a a en fn(a) { some_function(..., a, ...) }.

domingo, 14 de abril de 2024

Pattern Matching en Erlang parte 2


Iniciemos un nuevo módulo llamado funciones en el que escribiremos un montón de funciones para explorar Pattern Matching:


-module(functions).

-compile(export_all). %% replace with -export() later, for God's sake!


La primera función que escribiremos es head/1, que actúa exactamente como erlang:hd/1, que toma una lista como argumento y devuelve su primer elemento. Se hará con la ayuda del operador | :


head([H|_]) -> H.


Para obtener el segundo elemento de una lista crearíamos la función:


second([_,X|_]) -> X.


Erlang simplemente deconstruirá la lista con Pattern Matching

1> c(functions).

{ok, functions}

2> functions:head([1,2,3,4]).

1

3> functions:second([1,2,3,4]).

2


Crearemos una función same/2 que toma dos argumentos y dice si son idénticos:


same(X,X) ->

true;

same(_,_) ->

false.


Y es así de simple. Antes de explicar cómo funciona la función, repasaremos nuevamente el concepto de variables vinculadas y no vinculadas, por si acaso.

Las variables independientes son variables sin ningún valor asociado. Vincular una variable es simplemente adjuntar un valor a una variable independiente. En el caso de Erlang, cuando se desea asignar un valor a una variable que ya está vinculada, se produce un error a menos que el nuevo valor sea el mismo que el anterior. 

Volviendo a nuestro código: lo que sucede cuando llamas a Same(a,a) es que la primera X se ve como independiente: automáticamente toma el valor a. Luego, cuando Erlang pasa al segundo argumento, ve que X ya está vinculado. Luego lo compara con el a pasado como segundo argumento y mira para ver si coincide. La coincidencia de patrones se realiza correctamente y la función devuelve verdadero. Si los dos valores no son iguales, esto fallará y pasará a la segunda cláusula de función, a la que no le importan sus argumentos (cuando eres el último en elegir, ¡no puedes ser exigente!) y en su lugar lo hará. falso retorno. Tenga en cuenta que esta función puede aceptar cualquier tipo de argumento. Funciona para cualquier tipo de datos, no solo listas o variables individuales. Como ejemplo bastante avanzado, la siguiente función imprime una fecha, pero sólo si tiene el formato correcto:


valid_time({Date = {Y,M,D}, Time = {H,Min,S}}) ->

    io:format("The Date tuple (~p) says today is: ~p/~p/~p,~n",[Date,Y,M,D]),

    io:format("The time tuple (~p) indicates: ~p:~p:~p.~n", [Time,H,Min,S]);

valid_time(_) ->

   io:format("Stop feeding me wrong data!~n").


Tenga en cuenta que es posible utilizar el operador = en el encabezado de la función, lo que nos permite hacer coincidir tanto el contenido dentro de una tupla ({Y,M,D}) como la tupla en su conjunto. La función se puede probar de la siguiente manera:

4> c(functions).

{ok, functions}

5> functions:valid_time({{2011,09,06},{09,04,43}}).

The Date tuple ({2011,9,6}) says today is: 2011/9/6,

The time tuple ({9,4,43}) indicates: 9:4:43.

ok

6> functions:valid_time({{2011,09,06},{09,04}}).

Stop feeding me wrong data!

ok


¡Sin embargo hay un problema! Esta función podría tomar cualquier valor como valor, incluso texto o átomos, siempre que las tuplas tengan la forma {{A,B,C}, {D,E,F}}. Esto denota uno de los límites de Pattern Matching: puede especificar valores realmente precisos, como un número conocido de átomos, o valores abstractos, como el head o tail de una lista, una tupla de N elementos o cualquier cosa _ , etc. Para solucionar este problema utilizamos Guards.