sábado, 5 de noviembre de 2022

Un ejemplo de uso de Go routines


Un ejemplo sencillo de Go routines, tenemos 2 Slices con diferentes números y queremos que se impriman en go routines diferentes y bueno, tengo que poner un time.Sleep(time.Second) para esperar que termine. 

Debería haber usado un sync.WaitGroup pero no lo hice, igual hay un ejemplo acá.


package main

import (

"fmt"

"time"

"math/rand"

)


func main(){

s1, s2 := []int{1,2,3}, []int{4,5,6}

fmt.Println("Let's Race !!!!!!!!!!!!\n")

go printElemOfIntSlice(s1, 100)

go printElemOfIntSlice(s2, 100)

time.Sleep(time.Second)

}


func printElemOfIntSlice(slice []int, velo int){

for _, val  := range slice{

fmt.Println(val)

duration := rand.Intn(velo)

time.Sleep(time.Duration(duration) * time.Millisecond)

}

}

viernes, 4 de noviembre de 2022

Ejemplo de uso de sync.WaitGroup en Go

 


En Go cuando queremos ejecutar una corutina luego que se ejecuten un conjunto de corutinas, podemos utilizar waitgroup. 

En el ejemplo se ordena un slice dividido en 4 partes en 4 corutinas diferentes y se espera a que terminen. Espero que les sirva. 

package main


import (

 "fmt"

 "strconv"

 "sort"

 "math/rand"

 "math"

 "sync"

)


func main() {

fmt.Println("enter quantity of integers")

var nroStr string

fmt.Scan(&nroStr)

nro, err := strconv.Atoi(nroStr)

if (err != nil) {

return;

}

wg := sync.WaitGroup{}

nros := make([]int, 0, nro)

for i := 0; i < nro; i++ {

nros = append(nros,rand.Intn(10*nro))

}

if nro > 4 {

quart := int(math.RoundToEven(float64(nro) / 4.0))

wg.Add(4)

for i:=0; i < 3; i++ {

start := (i * quart)

end := (((i + 1) * quart))

go sortNros(nros[start:end], &wg)

}

go sortNros(nros[(3 * quart) : nro], &wg)

} else {

wg.Add(1)

go sortNros(nros, &wg)

}

wg.Wait()

fmt.Println(nros)

}


func sortNros(nros []int, wg *sync.WaitGroup) {

sort.Ints(nros)

wg.Done()

}

    


jueves, 3 de noviembre de 2022

Interfaces vs. generics en Go




Habiamos visto en post anterior la función de los generics. Por lo tanto sigamos profundizando su relación con las interfaces. 

A diferencia de los genéricos en C++, los genéricos de Go solo pueden realizar operaciones específicas enumeradas en una interfaz, esta interfaz se conoce como restricción.

El compilador utiliza una restricción para asegurarse de que el tipo proporcionado para la función admita todas las operaciones realizadas por los valores instanciados mediante el parámetro de tipo.

Por ejemplo, en el siguiente fragmento, cualquier valor del parámetro de tipo T solo es compatible con el método String; puede usar len() o cualquier otra operación sobre él.


// Stringer is a constraint

type Stringer interface {

 String() string

}


// Here T has to implement Stringer, T can only perform operations defined by Stringer

func stringer[T Stringer](s T) string {

 return s.String()

}


Go permite tipos predefinidos como int y string para implementar interfaces que se usan en restricciones. Estas interfaces con tipos predefinidos solo se pueden usar como una restricción.


type Number {

  int

}

No se puede usar una restricción con un tipo y método predefinidos, ya que los tipos predefinidos no tienen métodos en estos tipos definidos; por lo tanto, es imposible implementar estas restricciones.


type Number {

  int

  Name() string // int no tiene el metodo Name

}


El operador | permite una unión de tipos (es decir, múltiples tipos concretos pueden implementar la interfaz única y la interfaz resultante permite operaciones comunes en todos los tipos de unión).


type Number interface {

        int | int8 | int16 | int32 | int64 | float32 | float64

}


En el ejemplo anterior, la interfaz Number ahora admite todas las operaciones que son comunes en el tipo proporcionado, como <,> y +; todas las operaciones algorítmicas son compatibles con la interfaz Number.


func Min[T Number](x, y T) T {

        if x < y {

                return x

        }

        return y

}


El uso de una unión de varios tipos permite realizar operaciones comunes admitidas por estos tipos y escribir código que funcione para todos los tipos en unión.


Go permite crear tipos definidos por el usuario a partir de tipos predefinidos como int, string, etc. Los operadores ~ nos permiten especificar que la interfaz también admite tipos con los mismos tipos subyacentes.


Por ejemplo, si desea agregar soporte para el tipo Punto con el tipo subrayado int a la función Min; esto es posible usando ~.


// Any Type with given underlying type will be supported by this interface

type Number interface {

        ~int | ~int8 | ~int16 | ~int32 | ~int64 | ~float32 | ~float64

}


// Type with underlying int

type Point int


func Min[T Number](x, y T) T {

        if x < y {

                return x

        }

        return y

}


func main() {

        // creating Point type

        x, y := Point(5), Point(2)


        fmt.Println(Min(x, y))


}


Todos los tipos predefinidos admiten este tipo aproximado: el operador ~ solo funciona con restricciones.


// Union operator and type approximation both use together without interface

func Min[T ~int | ~float32 | ~float64](x, y T) T {

        if x < y {

                return x

        }

        return y

}


Las restricciones también admiten la anidación; la restricción de número se puede construir a partir de la restricción de entero y la restricción de flotación.


// Integer is made up of all the int types

type Integer interface {

        ~int | ~int8 | ~int16 | ~int32 | ~int64

}


// Float is made up of all the float type

type Float interface {

        ~float32 | ~float64

}


// Number is build from Integer and Float

type Number interface {

        Integer | Float

}


// Using Number

func Min[T Number](x, y T) T {

        if x < y {

                return x

        }

        return y

}

El equipo de Go ha proporcionado un nuevo paquete con una colección de restricciones útiles; este paquete contiene restricciones para Integer, Float, etc.

Este paquete exporta restricciones para tipos predefinidos. Dado que se pueden agregar nuevos tipos predefinidos al lenguaje, es mejor usar las restricciones definidas en el paquete de restricciones. La más importante de ellas es la restricción Ordered https://pkg.go.dev/golang.org/x/exp/constraints#Ordered). Define todos los tipos que admiten los operadores >,<,== y !=.

func min[T constraints.Ordered](x, y T) T {
    if x > y {
        return x
    } else {
        return y
    }
}

Los genéricos no reemplazan a las interfaces. Los genéricos están diseñados para funcionar con interfaces y hacer que Go sea más seguro, y también se pueden usar para eliminar la repetición de código.

La interfaz representa un conjunto del tipo que implementa la interfaz, mientras que los genéricos son un marcador de posición para los tipos reales. Durante la compilación, el código genérico puede convertirse en una implementación basada en interfaz.



martes, 1 de noviembre de 2022

Interface y polimorfismo en Go

 


En Go no existen las clases pero si existe la programación orientada a objetos, veamos un ejemplo


package main


import (

 "fmt"

 "bufio"

 "os"

 "strings"

)


type Animal interface {

Eat()

Move()

Speak()

GetName() string

}


type Cow struct { name string }


func (c Cow) Eat() {

fmt.Println("grass")

}


func (c Cow) Move() {

fmt.Println("walk")

}


func (c Cow) Speak() {

fmt.Println("moo")

}


func (c Cow) GetName() string {

return c.name

}



type Bird struct { name string }


func (c Bird) Eat() {

fmt.Println("worms")

}


func (c Bird) Move() {

fmt.Println("fly")

}


func (c Bird) Speak() {

fmt.Println("peep")

}


func (c Bird) GetName() string {

return c.name

}


type Snake struct { name string }


func (c Snake) Eat() {

fmt.Println("mice")

}


func (c Snake) Move() {

fmt.Println("slither")

}


func (c Snake) Speak() {

fmt.Println("hsss")

}


func (c Snake) GetName() string {

return c.name

}


func main() {


animals := make([]Animal, 0, 0)

var animal *Animal

for {

    fmt.Print("< ")

inputStr := get_input()

input := strings.Split(inputStr, " ")

if (len(input) == 1 && input[0] == "quit") {

break

if len(input) == 3 && input[0] == "newanimal" {

aniStr := input[2]

switch aniStr {

case "cow":

animals = append(animals,Cow{ input[1] })

fmt.Println(" Created it! ")

case "bird":

       animals = append(animals,Bird{ input[1] })

fmt.Println(" Created it! ")

case "snake":

animals = append(animals,Snake{ input[1] })

fmt.Println(" Created it! ")

default:

fmt.Println("incorrect input animal")

}

}

if len(input) == 3 && input[0] == "query" {

    animal = find(animals, input[1])

if animal == nil {

fmt.Println("incorrect animal name")

} else {

actionStr := input[2]

switch actionStr {

case "eat":

(*animal).Eat()

case "move":

(*animal).Move()

case "speak":

(*animal).Speak()

default:

fmt.Println("incorrect input action")

break

}

}

}

}


func find(animals []Animal, name string) *Animal {

for i := 0; i < len(animals); i++ {

if animals[i].GetName() == name {

return &animals[i]

}

}

return nil

}


func get_input() string {

    

  var input_string string

    var input []byte

    var err error

    

    for {

        

        reader := bufio.NewReader(os.Stdin)


        input, _ , err = reader.ReadLine()

        

        if err != nil{

            fmt.Println("Something going wrong! Try again")

        } else { 

    input_string = string(input)

            break

        } 

    }

    return input_string

}

    


Generics en Go


Go 1.18 soporta generics. Espero que sepan que es generics, pero para hacerla corta, es la forma en la que en lenguajes de tipado estático podemos hacer funciones o clases (si el lenguaje lo soporta) que hacen cosas con un tipo genérico. Por ejemplo una lista, se comporta como lista sin importar si es una lista de personas, números, letras o choripanes. Siempre se comporta como una lista y punto. 

Entonces, Go 1.18.0 presenta una nueva sintaxis para proporcionar metadatos adicionales sobre tipos y definir restricciones en estos tipos. Veamos un ejemplo : 


package main

import "fmt"


func main() {

        fmt.Println(reverse([]int{1, 2, 3, 4, 5}))

}


// T es un parámetro de tipo que se usa como tipo normal dentro de la función

// any es una restricción en el tipo, es decir, T tiene que implementar la interfaz "any"

func reverse[T any](s []T) []T {

        l := len(s)

        r := make([]T, l)

        for i, ele := range s {

                r[l-i-1] = ele

        }

        return r

}


Los corchetes [] se usan para especificar parámetros de tipo, que son una lista de identificadores y una interfaz de restricción.  T es un parámetro de tipo que se usa para definir el tipo del argumento y el tipo de retorno de la función.

El parámetro también es accesible dentro de la función. any es una interfaz; T tiene que implementar esta interfaz. Go 1.18 presenta any como un alias para la interfaz{}.

El parámetro de tipo es como una variable de tipo: todas las operaciones admitidas por tipos normales son compatibles con variables de tipo (por ejemplo, hacer función). La variable inicializada usando estos parámetros de tipo soportará la operación de la restricción; en el ejemplo anterior, la restricción es la interfaz any.

La función tiene un tipo de retorno []T y un tipo de entrada de []T. Aquí, el parámetro de tipo T se usa para definir más tipos que se usan dentro de la función. Estas funciones genéricas se instancian pasando el valor de tipo al parámetro de tipo.

reverseInt:= reverse[int]

El compilador de Go infiere el parámetro de tipo comprobando los argumentos pasados a las funciones. En el ejemplo, infiere automáticamente que el parámetro de tipo es int y, a menudo, puede omitirlo.


// without passing type

fmt.Println(reverse([]int{1, 2, 3, 4, 5}))

// passing type

fmt.Println(reverse[int]([]int{1, 2, 3, 4, 5}))


El uso de parámetros de tipo en funciones permite a los programadores escribir códigos genéricos sobre tipos.

El compilador creará una definición separada para cada combinación de tipos pasados en la creación de instancias o creará una definición basada en interfaz derivada de patrones de uso y algunas otras condiciones.

Al crear un Slice, solo se requiere un tipo, por lo que solo se necesita un parámetro de tipo. Veamos un ejemplo: 


func ForEach[T any](s []T, f func(ele T, i int , s []T)){

    for i,ele := range s {

        f(ele,i,s)

    }

}

El mapa requiere dos tipos, un tipo para la clave y un tipo el valor. El tipo de valor no tiene restricciones, pero el tipo de clave siempre debe satisfacer la restricción comparable.

func keys[K comparable, V any](m map[K]V) []K {

    key := make([]K, len(m))

    i := 0

    for k, _ := range m {

        key[i] = k

        i++

    }

    return key

}

Del mismo modo, los canales también son compatibles con los genéricos.

Go permite definir estructuras con un parámetro de tipo. La sintaxis es similar a la función genérica. El parámetro de tipo se puede usar en el método y los miembros de datos en la estructura.


// T is type parameter here, with any constraint

type MyStruct[T any] struct {

    inner T

}


// No new type parameter is allowed in struct methods

func (m *MyStruct[T]) Get() T {

    return m.inner

}

func (m *MyStruct[T]) Set(v T) {

    m.inner = v

}


No se permite definir nuevos parámetros de tipo en los métodos de estructura, pero los parámetros de tipo definidos en las definiciones de estructura se pueden usar en los métodos.

Los tipos genéricos se pueden anidar dentro de otros tipos. El parámetro de tipo definido en una función o estructura se puede pasar a cualquier otro tipo con parámetros de tipo.


type Enteries[K, V any] struct {

    Key   K

    Value V

}

func enteries[K comparable, V any](m map[K]V) []*Enteries[K, V] {

    // define a slice with Enteries type passing K, V type parameters

    e := make([]*Enteries[K, V], len(m))

    i := 0

    for k, v := range m {

        // creating value using new keyword

        newEntery := new(Enteries[K, V])

        newEntery.Key = k

        newEntery.Value = v

        e[i] = newEntery

        i++

    }

    return e

}


func enteries[K comparable, V any](m map[K]V) []*Enteries[K, V]


A diferencia de los genéricos en C++, los genéricos de Go solo pueden realizar operaciones específicas enumeradas en una interfaz, esta interfaz se conoce como restricción.

El compilador utiliza una restricción para asegurarse de que el tipo proporcionado para la función admita todas las operaciones realizadas por los valores instanciados mediante el parámetro de tipo.

Por ejemplo, en el siguiente fragmento, cualquier valor del parámetro de tipo T solo es compatible con el método String.

// Stringer is a constraint

type Stringer interface {

 String() string

}


// Here T has to implement Stringer, T can only perform operations defined by Stringer

func stringer[T Stringer](s T) string {

 return s.String()

}

Puff que me quedo relargo el post, y todavia hay algunos detalles que se me escapan que seguire explorando en otros posts. 


Dejo link: 

https://go.dev/doc/tutorial/generics

https://gobyexample.com/generics

sábado, 29 de octubre de 2022

Apache Kafka 3.3 reemplaza a ZooKeeper con el nuevo protocolo KRaft


Tal vez lo más molesto de instalar y levantar kafka para un desarrollador, es instalar y levantar zookeeper. No porque sea difícil, sino que no se entiende mucho la utilidad en entornos de desarrollo. 

Apache Software Foundation ha lanzado Apache Kafka 3.3.1 con muchas funciones y mejoras nuevas. En particular, esta es la primera versión que marca el protocolo KRaft (Kafka Raft) como listo para la producción. 

KRaft es el protocolo de consenso desarrollado para permitir la gestión de metadatos directamente en Apache Kafka. Esto simplifica enormemente la arquitectura de Kafka al consolidar la responsabilidad de los metadatos en Kafka mismo sin el requisito de una herramienta de terceros como Apache ZooKeeper. Este nuevo modo KRaft mejora la escalabilidad y la resiliencia de la partición al tiempo que simplifica las implementaciones de Apache Kafka que ahora se pueden implementar de forma independiente.

Dejo link: https://www.mail-archive.com/announce@apache.org/msg07620.html

jueves, 27 de octubre de 2022

Libros gratuitos de java code geeks

 

Get schooled by Java Code Geeks

Download FREE IT Guides!

 

Java Annotations Tutorial

We have provided an abundance of tutorials here at Java Code Geeks, like Creating Your Own Java Annotations, Java Annotations Tutorial with Custom Annotation and Java...

 
 

Spring Interview Questions

The majority of the things you may be asked is collected in the list below. All core modules, from basic Spring functionality such as Spring Beans, up to Spring MVC framework are...

 
 

Android UI Design

Android’s user interface is based on direct manipulation, using touch inputs that loosely correspond to real-world actions, like swiping, tapping, pinching and reverse pinching to...

 
 

JPA Minibook

The basic Java framework to access the database is JDBC. Unfortunately, with JDBC, a lot of hand work is needed to convert a database query result into Java classes. Other disadvantages...

 

domingo, 23 de octubre de 2022

Funciones en Go parte 2


Seguimos con funciones en Go. 

Se puede pasar la dirección de una variable como parámetro de una función con esta técnica, podemos modificar el valor este parámetro. A la vez, esto permite que golang no tenga la necesidad de copiar este parámetro. Cosa muy útil cuando tenemos que pasar estructuras grandes o vectores. 

package main

import "fmt"


func update(a *int, t *string) {

*a = *a + 5      // defrencing pointer address

*t = *t + " Doe" // defrencing pointer address

return

}


func main() {

var age = 20

var text = "John"

fmt.Println("Before:", text, age)

update(&age, &text)

fmt.Println("After :", text, age)

}


Una función anónima es una función que se declaró sin ningún identificador con nombre para hacer referencia a ella. Las funciones anónimas pueden aceptar entradas y devolver salidas, tal como lo hacen las funciones estándar.

package main


import "fmt"


func main() {

func(l int, b int) {

fmt.Println(l * b)

}(20, 30)

}


Las clausuras son un caso especial de funciones anónimas., son funciones anónimas que acceden a las variables definidas fuera del cuerpo de la función.


package main

import "fmt"


func main() {

for i := 10.0; i < 100; i += 10.0 {

rad := func() float64 {

return i * 39.370

}()

fmt.Printf("%.2f Meter = %.2f Inch\n", i, rad)

}

}


Una función de orden superior es una función que recibe una función como argumento o devuelve la función como salida.

Las funciones de orden superior son funciones que operan sobre otras funciones, ya sea tomándolas como argumentos o devolviéndolas.


package main

import "fmt"


func sum(x, y int) int {

return x + y

}

func partialSum(x int) func(int) int {

return func(y int) int {

return sum(x, y)

}

}

func main() {

partial := partialSum(3)

fmt.Println(partial(7))

}


En el programa anterior, la función de suma parcial devuelve una función de suma que toma dos argumentos int y devuelve un argumento int.


package main

import "fmt"


func squareSum(x int) func(int) func(int) int {

return func(y int) func(int) int {

return func(z int) int {

return x*x + y*y + z*z

}

}

}

func main() {

// 5*5 + 6*6 + 7*7

fmt.Println(squareSum(5)(6)(7))

}


En el programa anterior, la firma de la función squareSum que especifica esa función devuelve dos funciones y un valor entero.

Golang también admite definir nuestros propios tipos de funciones.

La versión modificada del programa anterior con tipos de funciones como se muestra a continuación:


package main

import "fmt"


type First func(int) int

type Second func(int) First


func squareSum(x int) Second {

return func(y int) First {

return func(z int) int {

return x*x + y*y + z*z

}

}

}


func main() {

// 5*5 + 6*6 + 7*7

fmt.Println(squareSum(5)(6)(7))

}



Google Carbon el remplazo de C++


Google anuncio el nuevo lenguaje que remplazará a C++. Según google, es muy difícil que Rust remplace a C++, por lo tanto él invento Carbon.  Por ahí, se olvida que hace varios años, google había dicho que Go remplazaría a C++, pero bueno. A mi entender, estas cosas suceden no se pueden planear, es decir, veo muy difícil que de forma planeada, un lenguaje remplace a otro, porque esto depende mucho de las personas que usan el lenguaje y no del lenguaje en sí. De esta manera, un lenguaje toma el lugar de otro, no porque una empresa lo diga, sino porque programador a programador deciden migrar. 

Si van a el github de Carbon, no vamos a encontrar mucha información. Solo algunos ejemplos, veamos uno : 


Como objetivos del lenguaje, el principal es remplazar a C++. C++ sigue siendo el lenguaje de programación dominante para el software de rendimiento crítico, con bases de código e inversiones masivas y crecientes. Sin embargo, está luchando por mejorar y satisfacer las necesidades de los desarrolladores, en gran parte debido a la acumulación de décadas de deuda técnica. Mejorar gradualmente C++ es extremadamente difícil, tanto por la deuda técnica en sí misma como por los desafíos con su proceso de evolución. La mejor manera de abordar estos problemas es evitar heredar el legado de C o C++ directamente y, en su lugar, comenzar con bases de lenguaje sólidas, como un sistema genérico moderno, una organización de código modular y una sintaxis simple y coherente.

Los lenguajes modernos existentes ya brindan una excelente experiencia de desarrollador: Go, Swift, Kotlin, Rust y muchos más. Los desarrolladores que pueden usar uno de estos lenguajes existentes deberían hacerlo. Desafortunadamente, los diseños de estos lenguajes presentan barreras significativas para la adopción y migración desde C++. Estas barreras van desde cambios en el diseño idiomático del software hasta gastos generales de rendimiento.

Carbon es fundamentalmente un enfoque de lenguaje sucesor, en lugar de un intento de evolucionar gradualmente a C++. Está diseñado en torno a la interoperabilidad con C++, así como a la adopción y migración a gran escala para las bases de código y los desarrolladores de C++ existentes. Un lenguaje sucesor de C++ requiere:

  • Coincidencia de rendimiento con C++, una propiedad esencial para nuestros desarrolladores.
  • Interoperabilidad bidireccional transparente con C++, de modo que una biblioteca en cualquier parte de una pila de C++ existente puede adoptar Carbon sin portar el resto.
  • Una curva de aprendizaje suave con una familiaridad razonable para los desarrolladores de C++.
  • Expresividad comparable y compatibilidad con el diseño y la arquitectura del software existente.
  • Migración escalable, con cierto nivel de traducción de fuente a fuente para código C++ idiomático.

Funciones en Go


Una función es un grupo de declaraciones que existen dentro de un programa con el propósito de realizar una tarea específica. En un nivel alto, una función toma una entrada y devuelve una salida.

La función permite extraer bloques de código de uso común en un solo componente.

La función más popular en Go es main(), que se usa en todos los programas y es el punto de entrada de la ejecución. 

Una declaración de función comienza con la palabra clave func, seguida del nombre de la función, un par de paréntesis () que son los parámetros y luego un bloque que contiene el código de la función.

El siguiente ejemplo tiene una función con el nombre SimpleFunction. No toma ningún parámetro y no devuelve valores.


package main

import "fmt"

// SimpleFunction prints a message

func SimpleFunction() {

fmt.Println("Hello World")

}

func main() {

SimpleFunction()

}


Los argumentos o parámetros se especifican después del nombre de la función, entre paréntesis. Puede agregar tantos argumentos como se necesite, simplemente hay que separarlos con una coma.

El siguiente ejemplo tiene una función con dos argumentos de tipo int. Cuando se llama a la función add(), pasamos dos valores enteros (por ejemplo, 20,30).


package main

import "fmt"


// Function accepting arguments

func add(x int, y int) {

total := 0

total = x + y

fmt.Println(total)

}


func main() {

// Passing arguments

add(20, 30)

}


Si las funciones con nombres que comienzan con una letra mayúscula se exportarán a otros paquetes. Si el nombre de la función comienza con una letra minúscula, no se exportará a otros paquetes, pero puede llamar a esta función dentro del mismo paquete.

En este ejemplo, la función add() toma la entrada de dos números enteros y devuelve un valor entero con el nombre de total.

La declaración de devolución es necesaria cuando se declara un valor de devolución como parte de la firma de la función. Es decir, si digo que va devolver un tipo tengo que poner "return" y retornar un valor del tipo declarado. 


package main

import "fmt"


// Function with int as return type

func add(x int, y int) int {

total := 0

total = x + y

return total

}


func main() {

// Accepting return value in varaible

sum := add(20, 30)

fmt.Println(sum)

}


Los tipos de entrada y valor de retorno deben coincidir con la firma de la función. Si modificamos el programa anterior y pasamos algún valor de cadena en el argumento, el programa arrojará una excepción.

Golang permite nombrar los valores de retorno de una función. En este ejemplo nombramos la variable de retorno como area: 


package main

import "fmt"


func rectangle(l int, b int) (area int) {

var parameter int

parameter = 2 * (l + b)

fmt.Println("Parameter: ", parameter)

area = l * b

return // Return statement without specify variable name

}


func main() {

fmt.Println("Area: ", rectangle(20, 30))

}


Dado que la función se declara para devolver un valor de tipo int, la última declaración lógica en el flujo de ejecución debe ser una declaración de devolución que devuelva un valor del tipo declarado.

Las funciones en Golang pueden devolver múltiples valores, lo cual es una característica útil en muchos escenarios prácticos.

Este ejemplo declara una función con dos valores de retorno y la llama desde una función principal.

package main


import "fmt"


func rectangle(l int, b int) (area int, parameter int) {

parameter = 2 * (l + b)

area = l * b

return // Return statement without specify variable name

}


func main() {

var a, p int

a, p = rectangle(20, 30)

fmt.Println("Area:", a)

fmt.Println("Parameter:", p)

}

Dejo link: https://gobyexample.com/functions

sábado, 22 de octubre de 2022

Go by example


Quiero recomendarles esta pagina, que creo que ya la recomendé anteriormente,  pero como no me acuerdo, lo vuelvo a hacer. 

Los que estamos aprendiendo go, nos vienen bien siempre unos ejemplos. 

https://gobyexample.com/

viernes, 21 de octubre de 2022

Transformadores de mónadas en cats parte 5


El uso generalizado de transformadores de mónadas a veces es difícil porque fusionan las mónadas de formas predefinidas. Sin una reflexión cuidadosa, podemos terminar teniendo que desempaquetar y volver a empaquetar mónadas en diferentes configuraciones para operar con ellas en diferentes contextos.

Podemos hacer frente a esto de múltiples maneras. Un enfoque consiste en crear una sola "súper pila" y adherirse a ella en toda nuestra base de código. Esto funciona si el código es simple y en gran medida de naturaleza uniforme. Por ejemplo, en una aplicación web, podríamos decidir que todos los controladores de solicitudes son asíncronos y todos pueden fallar con el mismo conjunto de códigos de error HTTP. Podríamos diseñar un ADT personalizado que represente los errores y usar una fusión Future y Either en todas partes de nuestro código:

sealed abstract class HttpError

final case class NotFound(item: String) extends HttpError

final case class BadRequest(msg: String) extends HttpError

// etc...

type FutureEither[A] = EitherT[Future, HttpError, A]


El enfoque de "súper pila" comienza a fallar en bases de código más grandes y heterogéneas donde las diferentes pilas tienen sentido en diferentes contextos. Otro patrón de diseño que tiene más sentido en estos contextos utiliza transformadores de mónadas como "código de unión" local. Exponemos las pilas no transformadas en los límites del módulo, las transformamos para operarlas localmente y las destransformamos antes de pasarlas. Esto permite que cada módulo de código tome sus propias decisiones sobre qué transformadores usar:


import cats.data.Writer

type Logged[A] = Writer[List[String], A]

// Methods generally return untransformed stacks:

def parseNumber(str: String): Logged[Option[Int]] = util.Try(str.toInt).toOption match {

     case Some(num) => Writer(List(s"Read $str"), Some(num))

     case None => Writer(List(s"Failed on $str"), None)

}

// Consumers use monad transformers locally to simplify composition:

def addAll(a: String, b: String, c: String): Logged[Option[Int]] = {

    import cats.data.OptionT

    val result = for {

        a <- OptionT(parseNumber(a))

        b <- OptionT(parseNumber(b))

        c <- OptionT(parseNumber(c))

    } yield a + b + c

    result.value

}

// This approach doesn't force OptionT on other users' code:

val result1 = addAll("1", "2", "3")

// result1: Logged[Option[Int]] = WriterT(

//    (List("Read 1", "Read 2", "Read 3"), Some(6))

// )

val result2 = addAll("1", "a", "3")

// result2: Logged[Option[Int]] = WriterT(

//    (List("Read 1", "Failed on a"), None)

// )


Desafortunadamente, no existen enfoques únicos para trabajar con transformadores de mónadas. El mejor enfoque para usted puede depender de muchos factores: el tamaño y la experiencia de su equipo, la complejidad de su código base, etc. Es posible que deba experimentar y recopilar comentarios de colegas para determinar si los transformadores de mónadas son una buena opción.

martes, 18 de octubre de 2022

Transformadores de mónadas en cats parte 4

Muchas mónadas en Cats se definen usando el transformador correspondiente y la mónada Id. Esto es tranquilizador ya que confirma que las API para mónadas y transformadores son idénticas. Reader, Writer, y State se definen de esta manera:

type Reader[E, A] = ReaderT[Id, E, A] 

type Writer[W, A] = WriterT[Id, W, A]

type State[S, A] = StateT[Id, S, A]

En otros casos, los transformadores de mónadas se definen por separado de sus correspondientes mónadas. En estos casos, los métodos del transformador tienden a reflejar los métodos de la mónada. Por ejemplo, OptionT define getOrElse, y EitherT define fold, bimap, swap y otros métodos útiles.

domingo, 16 de octubre de 2022

Transformadores de mónadas en cats parte 3


Como vimos anteriormente, podemos crear pilas de mónadas transformadas utilizando el método de apply del transformador  o la sintaxis pure :

 

// Create using apply:

val errorStack1 = OptionT[ErrorOr, Int](Right(Some(10)))

// errorStack1: OptionT[ErrorOr, Int] = OptionT(Right(Some(10)))

// Create using pure:

val errorStack2 = 32.pure[ErrorOrOption]

// errorStack2: ErrorOrOption[Int] = OptionT(Right(Some(32)))


Una vez que hayamos terminado con una pila de transformadores de mónadas, podemos desempaquetarla usando el método value. Esto devuelve la pila sin transformar. Entonces podemos manipular las mónadas individuales de la manera habitual:


// Extracting the untransformed monad stack:

errorStack1.value

// res4: ErrorOr[Option[Int]] = Right(Some(10))

// Mapping over the Either in the stack:

errorStack2.value.map(_.getOrElse(-1))

// res5: Either[String, Int] = Right(32)


Cada llamada a value desempaqueta un solo transformador de mónada. Es posible que necesitemos más de una llamada para descomprimir por completo una gran pila. Por ejemplo:


futureEitherOr

// res6: FutureEitherOption[Int] = OptionT(

// EitherT(Future(Success(Right(Some(42))))))

val intermediate = futureEitherOr.value

// intermediate: FutureEither[Option[Int]] = EitherT(Future(Success(Right(Some(42)))))

val stack = intermediate.value

// stack: Future[Either[String, Option[Int]]] = Future(Success(Right(Some(42))))

Await.result(stack, 1.second)

// res7: Either[String, Option[Int]] = Right(Some(42))

sábado, 15 de octubre de 2022

Transformadores de mónadas en cats parte 2


Por convención, en Cats una mónada Foo tendrá una clase transformadora llamada FooT. De hecho, muchas mónadas en Cats se definen combinando un transformador de mónadas con la mónada Id. Concretamente, algunas de las instancias disponibles son:

  • cats.data.OptionT for Option;
  • cats.data.EitherT for Either;
  • cats.data.ReaderT for Reader;
  • cats.data.WriterT for Writer;
  • cats.data.StateT for State;
  • cats.data.IdT for the Id monad.

Todos estos transformadores de mónadas siguen la misma convención. El transformador en sí representa la mónada interna en una pila, mientras que el primer parámetro de tipo especifica la mónada externa. Los parámetros de tipo restantes son los tipos que hemos usado para formar las mónadas correspondientes.

Por ejemplo, nuestro tipo ListOption del ejemplo anterior es un alias para OptionT[List, A] pero el resultado es efectivamente una List[Option[A]]. En otras palabras, construimos pilas de mónadas de adentro hacia afuera:

type ListOption[A] = OptionT[List, A]

Muchas mónadas y todos los transformadores tienen al menos dos parámetros de tipo, por lo que a menudo tenemos que definir alias de tipo para etapas intermedias. Por ejemplo, supongamos que queremos envolver Either alrededor de Option. Option es el tipo más interno, por lo que queremos usar el transformador de mónada OptionT. Necesitamos usar Either como el primer parámetro de tipo. Sin embargo, Either dos parámetros de tipo y las mónadas solo tienen uno. Necesitamos un alias de tipo para convertir el constructor de tipo a la forma correcta:

// Alias Either to a type constructor with one parameter:
type ErrorOr[A] = Either[String, A]

// Build our final monad stack using OptionT:
type ErrorOrOption[A] = OptionT[ErrorOr, A]

ErrorOrOption es una mónada, al igual que ListOption. Podemos usar pure, map y flatMap como de costumbre para crear y transformar instancias:

import cats.instances.either._ // for Monad

val a = 10.pure[ErrorOrOption]
// a: ErrorOrOption[Int] = OptionT(Right(Some(10)))

val b = 32.pure[ErrorOrOption]
// b: ErrorOrOption[Int] = OptionT(Right(Some(32)))

val c = a.flatMap(x => b.map(y => x + y))
// c: OptionT[ErrorOr, Int] = OptionT(Right(Some(42)))

Las cosas se vuelven aún más confusas cuando queremos apilar tres o más mónadas. Por ejemplo, vamos a crear un Future de Either de Option. Una vez más, construimos esto de adentro hacia afuera con una OptionT de una EitherT de Future. Sin embargo, no podemos definir esto en una sola línea porque EitherT tiene tres parámetros de tipo:

case class EitherT[F[_], E, A](stack: F[Either[E, A]]) {
    // etc...
}

Los tres parámetros de tipo son los siguientes:
  • F[_] es la mónada externa en la pila (Either es la interna);
  • E es el tipo de error para el Either;
  • A es el tipo de resultado para Either.
Esta vez creamos un alias para EitherT que corrige Future y Error y permite que A varíe:

import scala.concurrent.Future
import cats.data.{EitherT, OptionT}

type FutureEither[A] = EitherT[Future, String, A]
type FutureEitherOption[A] = OptionT[FutureEither, A]

Nuestra gigantesca pila ahora compone tres mónadas y nuestros métodos map y flatMap atraviesan tres capas de abstracción:

import cats.instances.future._ // for Monad
import scala.concurrent.Await
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._

val futureEitherOr: FutureEitherOption[Int] =
    for {
        a <- 10.pure[FutureEitherOption]
        b <- 32.pure[FutureEitherOption]
    } yield a + b