Translate

Mostrando las entradas para la consulta clojure ordenadas por fecha. Ordenar por relevancia Mostrar todas las entradas
Mostrando las entradas para la consulta clojure ordenadas por fecha. Ordenar por relevancia Mostrar todas las entradas

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/

lunes, 25 de septiembre de 2023

Juego de Serie en Clojure


Cuando quiero aprender un nuevo lenguaje desarrollo un juego de series, es decir aparece una serie con un valor faltante y el jugador debe completarlo.

Uno de los requerimientos no funcionales es que se pueda agregar una serie nueva fácilmente.

Vamos a desarrollar este juego en Clojure:

Empecemos desarrollo de la serie, la serie tiene como responsabilidad generarse y si lo hacemos de esta manera podemos utilizar la potencia del polimorfismo, para que no quede acoplado la generación de la serie con el desarrollo del juego:

(ns secuencia-clojure.core

  (:gen-class))


(import '(java.util Scanner))

(def scan (Scanner. *in*))


   

(defn generateSecEven []

  (def seed (rand-int 30))

  (for [x (range seed (+ seed 4))] (* x 2))

)

   

(defn generateSecOdd []

  (def seed (rand-int 30))

  (for [x (range seed (+ seed 4))] (+ (* x 2) 1))

)


(defn generateSecFibo []

  (def seed (rand-int 30))

  (def seed2 (* seed 2))

  (def seed3 (* seed 3))

  (list seed seed seed2 seed3)

)

Y listo!! ahora en el main tenemos que llamar a esta función y ver si esta bien el resultado: 



(defn generateSec []
  (def seed (rand-int 3))
  (cond 
    (= seed 0) (generateSecEven)
    (= seed 1) (generateSecOdd)
    (= seed 2) (generateSecFibo)   
  )
)

(defn printSec [sec]
  (print (nth sec 0))
  (print " ")
  (print (nth sec 1))
  (print " ")
  (print " __ ")
  (print " ")
  (println (nth sec 3))
)

(defn game
  [points]
  (def sec (generateSec))
  (def sol (nth sec 2))
  
  (printSec sec)
  
  (let [data (read-line)]
  (if (= data (str sol))
    (do 
      (println "Correct!! the point is " (str (+ points 1)))
      (game (+ points 1))
    )
    (do 
      (println "Wrong!! the point is " (str (- points 1)))
      (game (- points 1))
    )))
)


(defn -main
  [& args]
  (game 0))

Y eso es todo a jugar se a dicho!!

Dejo el repositorio git: 

sábado, 9 de septiembre de 2023

Como crear un nuevo proyecto con Clojure y Leiningen?



Bueno, todo tiene un principio y vamos a empezar a crear un proyectito de consola con Clojure y Leiningen

$ lein new app my-project

Fijate vos que si queres utilizar nombre de proyecto con mayuscula, no te deja, ojo ahi. 

Ahora vamos a ejecutarlo :

$ lein run

podemos generar un jar con : 

$ lein jar

Si estamos trabajando en un proyecto de aplicación, Leiningen nos brinda la posibilidad de construir lo que se llama un uberjar. Este es un archivo JAR que contiene el proyecto en sí y todas las dependencias y está configurado para permitir que se ejecute tal cual.

$ lein uberjar

Compiling secuencia-clojure.core

$ java -jar target/uberjar/el-jar-standalone.jar 

Y ya podemos empezar!!

miércoles, 9 de agosto de 2023

Veamos Pedestal y Clojure


Clojure es un lenguaje de programación funcional y dinámico que se ejecuta en la máquina virtual de Java (JVM). Diseñado para ser simple, eficiente y expresivo, Clojure se basa en los principios de la programación funcional y proporciona herramientas para manejar la concurrencia y la inmutabilidad de manera efectiva. Su sintaxis concisa y su énfasis en la inmutabilidad lo convierten en una excelente elección para construir aplicaciones robustas y escalables.

Pedestal es un framework web desarrollado en Clojure que se enfoca en la construcción de aplicaciones web escalables y de alto rendimiento. A diferencia de algunos otros frameworks web, Pedestal no se basa en el paradigma de controladores y vistas, sino que se centra en la manipulación de datos y el flujo de información. Esto lo convierte en una opción ideal para aplicaciones modernas que requieren manejo eficiente de datos en tiempo real.

Las caracteristicas claves son: 

  • Modelo de Manejo de Datos: Pedestal se basa en el modelo de manejo de datos (data-driven) en lugar del enfoque tradicional de controladores y vistas. Esto permite una manipulación más eficiente y coherente de los datos a medida que fluyen a través de la aplicación.
  • Componentes Reutilizables: El framework fomenta la creación de componentes reutilizables que pueden ser ensamblados fácilmente para construir aplicaciones complejas. Esto mejora la modularidad y el mantenimiento del código.
  • Enfoque Asincrónico: Pedestal está diseñado para manejar concurrencia y operaciones asincrónicas de manera efectiva. Esto es esencial para aplicaciones que necesitan manejar múltiples solicitudes y actualizaciones en tiempo real.
  • Rendimiento Optimizado: Gracias a su enfoque asincrónico y a la optimización para el manejo eficiente de datos, Pedestal es capaz de ofrecer un alto rendimiento incluso en situaciones de alta carga.


La integración de Pedestal con Clojure es perfectamente natural, ya que ambos comparten la misma plataforma de ejecución (JVM) y una filosofía similar centrada en la simplicidad y la eficiencia. Al aprovechar las capacidades funcionales de Clojure, Pedestal puede manejar de manera elegante la manipulación de datos y el flujo de información en las aplicaciones web.


Vamos a hacer un hola mundo, para ello vamos a crear el proyecto con Leiningen : 


lein new pedestal-app hello-world


Esto creará una estructura de proyecto básica en el directorio hello-world.


Ejecutamos :

lein run

El servidor Pedestal se ejecutará en el puerto 8080 por defecto. Abre tu navegador web y navega a http://localhost:8080. Deberías ver el mensaje "¡Hola, Mundo desde Pedestal y Clojure!" en la página.

Dejo link: https://github.com/pedestal/pedestal

lunes, 5 de junio de 2023

Mi experiencia con la programación funcional en Scala


Creo que si leen este blog saben que me encanta la programación funcional y investigar y aprender. Y en eso estoy...


Me gustaría contar un poco mi evolución y mis altibajos para que la gente que sabe me aconseje y los que no saben puedan aprender de mis exitos y derrotas. 


Bueno, todo empezó con un curso de scala de coursera y quede encantado con el lenguaje y la programación funcional. De a poco tambien me interiorice en otros lenguajes como erlang, haskell, Clojure, Elixir, F#, etc ... pero siempre en la volvía a Scala por gusto nomas. Y me iba bastante bien...


Hasta que empece a quedarme corto en scala, quería progresar y decidí ver algunos framework funcionales, leí un libro de Cats y la verdad es que si bien esta bueno, me frustre mucho... porque no sabia porque se hacían ciertas cosas, no entendía porque tan complejo o si bien entendía desconfiaba ( no hay otra forma de hacerlo más fácil) y bueno ... 


Cambie a Akka, y me pareció bueno y me sentí más en mi mundo, pero justo justo cuando volvia el entuciasmo, cambio de licencia y bueno... Se fue el entusiasmo, ahora sé que existe un fork open source llamado pekko (https://github.com/apache/incubator-pekko) y es de apache. Pero lo veo verde, y no sé cuanto va a crecer ...


Y ahora estoy con ZIO y por ahora todo va bien, por ende la conclusión es si quieren progresar en su conocimiento en Scala, y no vienen del mundo funcional como Haskell, ZIO esta bueno. 

Si son nuevos en el mundo funcional y scala, yo haría lo siguiente: 

1. Estudiar Scala (curso de scala de coursera, el libro rojo de programación funcional en scala, etc ...)

2. Leer y estudiar ZIO 

3. Leer y estudiar Cats


Pero no perder de vista Pekko, para ver como progresa. 


Que opinan? 


lunes, 18 de julio de 2022

Cualidades clave de Kotlin


Estáticamente tipado
: al igual que Java, Kotlin es un lenguaje de programación con tipos estáticos. Esto significa que el tipo de cada expresión en un programa se conoce en el momento de la compilación, y el compilador puede validar que los métodos y campos a los que intenta acceder existen en los objetos que está utilizando. Esto contrasta con los lenguajes de programación tipificados dinámicamente, que están presentes en la JVM, entre otros, Groovy, Clojure y JRuby. Esos lenguajes le permiten definir variables y funciones que pueden almacenar o devolver datos de cualquier tipo y resolver las referencias de métodos y campos en tiempo de ejecución. Esto permite un código más corto y una mayor flexibilidad en la creación de estructuras de datos. Pero la desventaja es que los problemas como los nombres mal escritos no se pueden detectar durante la compilación y pueden provocar errores de tiempo de ejecución.

Por otro lado, a diferencia de Java, Kotlin no requiere que especifiques el tipo de cada variable explícitamente en tu código fuente. En muchos casos, el tipo de una variable se puede determinar automáticamente a partir del contexto, lo que le permite omitir la declaración de tipo. Aquí está el ejemplo más simple posible de esto:

val x = 1

Estás declarando una variable y, como se inicializa con un valor entero, Kotlin determina automáticamente que su tipo es Int. La capacidad del compilador para determinar tipos a partir del contexto se denomina inferencia de tipos.

Los siguientes son algunos de los beneficios del tipado estático:

  • Rendimiento: llamar a los métodos es más rápido porque no es necesario averiguar en tiempo de ejecución qué método debe llamarse.
  • Confiabilidad: el compilador verifica la corrección del programa, por lo que hay menos posibilidades de fallas durante el tiempo de ejecución.
  • Capacidad de mantenimiento: trabajar con código desconocido es más fácil porque puede ver con qué tipo de objetos está trabajando el código.
  • Compatibilidad con herramientas: la escritura estática permite refactorizaciones confiables, finalización de código precisa y otras características de IDE.

Gracias a la compatibilidad de Kotlin con la inferencia de tipos, la mayor parte de la verbosidad adicional asociada con la escritura estática desaparece porque no es necesario declarar los tipos explícitamente.

Si observa los detalles del sistema de tipos de Kotlin, encontrará muchos conceptos familiares. Las clases, las interfaces y los genéricos funcionan de manera muy similar a Java, por lo que la mayor parte de su conocimiento de Java debería transferirse fácilmente a Kotlin. Sin embargo, algunas cosas son nuevas.

El más importante de ellos es el soporte de Kotlin para tipos anulables, lo que le permite escribir programas más confiables al detectar posibles excepciones de puntero nulo en el momento de la compilación. 

Otra cosa nueva en el sistema de tipos de Kotlin es su soporte para tipos de funciones. Para ver de qué se trata, veamos las ideas principales de la programación funcional y veamos cómo se admite en Kotlin.

Programación funcional y orientada a objetos: Como desarrollador de Java, sin duda estamos familiarizado con los conceptos básicos de la programación orientada a objetos, pero la programación funcional puede ser nueva para desarrolladores Java. Los conceptos clave de la programación funcional son los siguientes:

  • Funciones de primera clase: Puede almacenar funciones en variables, pasarlos como parámetros o devolverlos desde otras funciones.
  • Inmutabilidad: trabaja con objetos inmutables, lo que garantiza que su estado no puede cambiar después de su creación.
  • Sin efectos secundarios: utiliza funciones puras que devuelven el mismo resultado dadas las mismas entradas y no modifican el estado de otros objetos ni interactúan con el mundo exterior.

¿Qué beneficios puede obtener al escribir código en el estilo funcional? Primero, concisión. El código funcional puede ser más elegante y sucinto en comparación con su contraparte imperativa, porque trabajar con funciones como valores le brinda mucho más poder de abstracción, lo que le permite evitar la duplicación en su código.

Imagine que tiene dos fragmentos de código similares que implementan una tarea similar (por ejemplo, buscar un elemento coincidente en una colección) pero difieren en los detalles (cómo se detecta el elemento coincidente). Puede extraer fácilmente la parte común de la lógica en una función y pasar las partes diferentes como argumentos. Esos argumentos son en sí mismos funciones, pero puede expresarlos usando una sintaxis concisa para funciones anónimas llamadas expresiones lambda:

fun findAlice() = findPerson { it.name == "Alice" }

fun findBob() = findPerson { it.name == "Bob" }

El segundo beneficio del código funcional es el subprocesamiento múltiple seguro. Una de las mayores fuentes de errores en los programas de subprocesos múltiples es la modificación de los mismos datos de varios subprocesos sin la sincronización adecuada. Si usa estructuras de datos inmutables y funciones puras, puede estar seguro de que tales modificaciones inseguras no ocurrirán y no necesita idear esquemas de sincronización complicados.

Finalmente, la programación funcional significa pruebas más fáciles. Las funciones sin efectos secundarios se pueden probar de forma aislada sin necesidad de una gran cantidad de código de configuración para construir todo el entorno del que dependen.

En términos generales, el estilo funcional se puede usar con cualquier lenguaje de programación, incluido Java, y muchas partes del mismo se recomiendan como un buen estilo de programación. Pero no todos los lenguajes brindan el soporte sintáctico y de biblioteca necesario para usarlo sin esfuerzo; por ejemplo, este soporte faltaba en su mayoría en las versiones de Java anteriores a Java 8. Kotlin tiene un amplio conjunto de características para admitir la programación funcional desde el principio. Estos incluyen lo siguiente:

  • Tipos de funciones, que permiten que las funciones reciban otras funciones como parámetros o devuelvan otras funciones
  • Expresiones lambda, que le permiten pasar bloques de código con un mínimo de repeticiones
  • Clases de datos, que proporcionan una sintaxis concisa para crear objetos de valor inmutable
  • Un amplio conjunto de API en la biblioteca estándar para trabajar con objetos y colecciones en el estilo funcional

Kotlin le permite programar en el estilo funcional pero no lo impone. Cuando lo necesite, puede trabajar con datos mutables y escribir funciones que tienen efectos secundarios sin tener que pasar por obstáculos adicionales. Y, por supuesto, trabajar con marcos que se basan en interfaces y jerarquías de clases es tan fácil como con Java. Al escribir código en Kotlin, puede combinar los enfoques funcional y orientado a objetos y utilizar las herramientas más apropiadas para el problema que está resolviendo.

Gratuito y open source: El lenguaje Kotlin, incluido el compilador, las bibliotecas y todas las herramientas relacionadas, es completamente de código abierto y de uso gratuito para cualquier propósito. Está disponible bajo la licencia Apache 2; el desarrollo ocurre abiertamente en GitHub (http://github.com/jetbrains/kotlin), y las contribuciones de la comunidad son bienvenidas. También puede elegir entre tres IDE de código abierto para desarrollar sus aplicaciones de Kotlin: IntelliJ IDEA Community Edition, Android Studio y Eclipse son totalmente compatibles. (Por supuesto, IntelliJ IDEA Ultimate también funciona).

Apis en diferentes lenguajes y frameworks en la plataforma Java


Estoy haciendo una aplicación con n microservicios en n tipos de tecnologías y lenguajes todos en la plataforma Java. 

Los de n tipos de tecnologías es mentiroso solo 2 framework use por ahora Spring boot y micronaut y a decir verdad use un 80% de spring boot pero quiero escribir un post tipo draft, por las dudas si me canso y nunca termino. 

Por ende, vamos a ver como me fue con Spring boot y diferentes lenguajes y luego hablo de micronaut. 

Java: Bueno, esta es la tupla más utilizada y creo que ustedes saben que anda muy bien. No sé si puedo agregar algo más que no sepan. 

Kotlin: Me encanto, por varias razones me parece mejor que java. Kotlin es un lenguaje, muy bueno y muy compatible con java lo que lo hace una opción super fácil de usar y no se sufre nada con la compatibilidad con spring boot. 

Groovy: Igual que Java, no encuentro una buena razón para elegir este lenguaje, esta bueno, pero el tipado dinamico, no ofrece mayor ventaja y si lo usamos con tipando estático, es similar a java o kotlin. 

Scala: Un infierno de incompatibilidad, no le recomiendo utilizar spring boot con scala, tenes que estar siempre cambiando tipos, de tipos de Scala a Java y de Java a Scala. No es una opción práctica...

Clojure: Otro infierno, no solo por los tipos, sino tambien por la generación de clases con anotaciones. Muy trabajoso, al final cuando te funciona una Api, no te acordas para que la querías. 

Micronaut lo utilice muy poco con Groovy y Java y me fue muy bien, casi casi puedo decir que me gusta más que spring boot. Y ahora sigo con Micronaut y Quarkus. 

jueves, 23 de junio de 2022

Agregar código java a un proyecto Clojure en Leiningen


 Quiero hacer unas clases en java y utilizarlas en mis funciones clojure. Es muy fácil, tengo que agregar   :java-source-paths a mi proyecto y ya toma las clases.  

Veamos un ejemplo

(defproject ejemplo "0.1.0-SNAPSHOT"

  :description "FIXME: write description"

  :url "http://example.com/FIXME"

  :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"

            :url "https://www.eclipse.org/legal/epl-2.0/"}

  :dependencies [[org.clojure/clojure "1.10.3"],

                 ...                 ]

  :java-source-paths ["src-java"]

  :main com.assembly.ejemplo.core

  :aot :all

  :target-path "target/%s"

  :profiles {:uberjar {:aot :all

                       :jvm-opts ["-Dclojure.compiler.direct-linking=true"]}})


Y listo!!

sábado, 18 de junio de 2022

Crear un proyecto Spring boot, con Leiningen y Clojure


La idea es crear un proyecto spring boot, con Clojure y Leiningen. (ya lo dice el titulo)

Primero hacemos un proyecto hello word con Leiningen (ojo tienen que tener Leiningen instalado, esta bueno instalarlo con sdkman, así :  sdk install leiningen) 

lein new app ejemplo

Luego agregamos las dependencias de spring boot al proyecto, para que quede algo así: 

(defproject github "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
            :url "https://www.eclipse.org/legal/epl-2.0/"}
  :dependencies [[org.clojure/clojure "1.10.3"],
                 [org.springframework.boot/spring-boot-starter-web "2.6.7"],
                 [org.springframework.boot/spring-boot-configuration-processor "2.6.7"]]
  :main com.assembly.example.core
  :aot :all
  :target-path "target/%s"
  :profiles {:uberjar {:aot :all
                       :jvm-opts ["-Dclojure.compiler.direct-linking=true"]}})

Ojo, tambien hay que agregar :aot y :all 

Ahora hacemos el Application, yo modifique el hello wold que vino por default, pero si lo hacen tengan cuidado de ponerlo en un paquete. 

(ns com.assembly.example.core
  (:import (org.springframework.boot SpringApplication))
  (:gen-class
    :name ^{org.springframework.boot.autoconfigure.SpringBootApplication []
            org.springframework.context.annotation.ComponentScan {:basePackages ["com.assembly.example"]}}
    com.assembly.example.core.Application
    :main true)
  )

(defn -main []
  (SpringApplication/run (Class/forName "com.assembly.example.core.Application") (into-array String '()))
  )

Y ahora hacemos un endpoint, en este ejemplo voy a hacer un hola mundo : 

(ns com.assembly.exanple.controller.greet

  (:import (org.springframework.web.bind.annotation PathVariable RequestMapping RequestMethod RestController)

           (org.springframework.http ResponseEntity))

  )


(gen-class

  :name ^{RestController {}

          RequestMapping {:value ["/v1/greet"]}} com.assembly.exanple.controller.greet.GreeterEndPoint

  :methods [[^{RequestMapping {:value ["/hello"]

                               :method [RequestMethod/GET]}} sayHello [] Object ]

            [^{RequestMapping {:value ["/helloto/{name}"]

                               :method [RequestMethod/GET]}} sayHelloTo [^{PathVariable {:value "name"}} String] Object ]

            ]

  :state injected

  :init init

)


(defn -init

  "Initialize the class by setting the state to an empty map, which can be populated with injected dependencies."

  []

  [[] (atom {})])


(defn -sayHello

  [this]

  (ResponseEntity/ok "Hello"))


(defn -sayHelloTo

  [this name]

  (ResponseEntity/ok (str "Hello " name)))


Y listo, si corremos el main, vamos a levantar el tomcat embebido de spring y vamos a : 

http://localhost:8080/v1/greet/hello

y veremos como nos saluda spring boot con clojure!! 

sábado, 11 de junio de 2022

Top de lenguajes en la JVM


Buscando que lenguajes están en el top 5 de más usados me encontré con esto en la wikipedia : 

As of April 2022, according to the TIOBE Index[1] of the top 100 programming languages, the top JVM languages are:

Lo que me sorprendio no fue la información sino que la wikipedia este tan actualizada. 

Otra cosa que me llamo la atención es que groovy le gane a scala o a clojure, pense que era diferente.

Y bueno, eso es todo, no me critiquen porque pegue algo en ingles en un blog en español, es de la wikipedia, no voy andar cambiando la fuente.  

Dejo link : https://en.wikipedia.org/wiki/List_of_JVM_languages

sábado, 29 de enero de 2022

Concurrencia en Clojure - parte 7


Seguimos con concurrencia en Clojure

Ahora podemos modificar nuestro servicio web para usar sesiones. Primero, necesitamos una función que cree una nueva sesión:

(defn create-session []

  (let [snippets (repeatedly promise)

    translations (delay (map translate

                         (strings->sentences (map deref snippets))))]

      (new-session {:snippets snippets :translations translations})))


Seguimos usando una secuencia perezosa infinita de promesas para representar los fragmentos entrantes y un mapa sobre esa secuencia para representar las traducciones, pero ahora ambos están almacenados en una sesión.

A continuación, debemos modificar accept-snippet y get-translation para buscar :snippets o :translations dentro de una sesión:

(defn accept-snippet [session n text]

  (deliver (nth (:snippets session) n) text))

(defn get-translation [session n]

  @(nth @(:translations session) n))


Finalmente, definimos las rutas que vinculan estas funciones a las URI:

(defroutes app-routes
  (POST "/session/create" []
    (response (str (create-session))))

  (context "/session/:session-id" [session-id]
    (let [session (get-session (edn/read-string session-id))]
      (routes
        (PUT "/snippet/:n" [n :as {:keys [body]}]
          (accept-snippet session (edn/read-string n) (slurp body))
          (response "OK"))

        (GET "/translation/:n" [n]
          (response (get-translation session (edn/read-string n))))))))

Esto nos brinda un servicio web que hace un uso juicioso de los datos mutables pero aún se siente principalmente funcional.


jueves, 27 de enero de 2022

Concurrencia en Clojure - parte 6



Vamos a crear un servicio web que maneje múltiples transcripciones introduciendo el concepto de una sesión. Cada sesión tiene un identificador numérico único, que se genera de la siguiente manera:

(def last-session-id (atom 0))

(defn next-session-id []

(swap! last-session-id inc))

Esto usa un átomo, last-session-id , que se incrementa cada vez que queremos una nueva ID de sesión. Como resultado, cada vez que se llama a next-session-id, devuelve un número uno más alto que el anterior:

server.core=> (in-ns 'server.session)
#<Namespace server.session>
server.session=> (next-session-id)
1
server.session=> (next-session-id)
2
server.session=> (next-session-id)
3

Vamos a hacer un seguimiento de las sesiones activas con otro átomo llamado sesiones que contiene un mapa de ID de sesión y valores de sesión:

(def sessions (atom {}))

(defn new-session [initial]

  (let [session-id (next-session-id)]

    (swap! sessions assoc session-id initial)

     session-id))


(defn get-session [id]

   (@sessions id))

Creamos una nueva sesión pasando un valor inicial a new-session, que obtiene una nueva ID de sesión y la agrega a las sesiones llamando a swap!. Recuper una sesión con get-session es una simple cuestión de buscarlo por su ID.

Si no vamos a aumentar continuamente la cantidad de memoria que usamos, necesitaremos alguna forma de eliminar las sesiones cuando ya no estén en uso. Nosotros podría hacer esto explícitamente (quizás con una función de eliminación de sesión), pero dado que estamos escribiendo un servicio web en el que no necesariamente podemos confiar en que los clientes limpien correctamente, vamos a implementar la caducidad de la sesión (expiry) en su lugar. Esto requiere un pequeño cambio en el código anterior:

(def sessions (atom {}))

(defn now []

  (System/currentTimeMillis))


(defn new-session [initial]

  (let [session-id (next-session-id)

         session (assoc initial :last-referenced (atom (now)))]

    (swap! sessions assoc session-id session)

    session-id))


(defn get-session [id]

  (let [session (@sessions id)]

     (reset! (:last-referenced session) (now))

     session))


Hemos agregado una función llamada now que devuelve la hora actual. Cuando new-session crea una sesión, agrega una entrada :last-referenced a la sesión, otro átomo que contiene la hora actual. Esto se actualiza con reset! cada vez que get-session accede a la sesión.

Ahora que cada sesión tiene una entrada :last-referenced, podemos caducar sesiones verificando periódicamente si alguna no ha sido referenciada por más de un cierto período de tiempo:


(defn session-expiry-time []

  (- (now) (* 10 60 1000)))


(defn expired? [session]

  (< @(:last-referenced session) (session-expiry-time)))


(defn sweep-sessions []

  (swap! sessions #(remove-vals % expired?)))


(def session-sweeper

  (schedule {:min (range 0 60 5)} sweep-sessions))


Esto utiliza la biblioteca Schejulure para crear un barrido de sesiones, que se ejecuten una vez cada cinco minutos. Cada vez que se ejecuta, elimina cualquier sesión que haya expirado.

miércoles, 26 de enero de 2022

Concurrencia en Clojure - parte 5




Imagina que queremos tener un átomo que nunca tenga un valor negativo. Podemos garantizar esto por medio de una función de validación cuando creamos el átomo:

user=> (def non-negative (atom 0 :validator #(>= % 0)))

#'user/non-negative

user=> (reset! non-negative 42)

42

user=> (reset! non-negative -1)

IllegalStateException Invalid reference state

Un validador es una función que se llama cada vez que se intenta cambiar el valor del átomo. Si devuelve verdadero, el intento puede tener éxito, pero si devuelve falso, el intento se abandonará.

El validador se llama antes de que se haya cambiado el valor del átomo y, al igual que la función que se pasa a swap!. Por lo tanto, los validadores tampoco deben tener efectos secundarios.

Los átomos también pueden tener observadores asociados con ellos:

user=> (def a (atom 0))

#'user/a

user=> (add-watch a :print #(println "Changed from " %3 " to " %4))

#<Atom@542ab4b1: 0>

user=> (swap! a + 2)

Changed from 0 to 2

2

La clave se utiliza para identificar al observador (así, por ejemplo, si hay varios observadores, podemos eliminar uno específico proporcionando la clave correspondiente). La función watch se llama cada vez que cambia el valor del átomo. Se le dan cuatro argumentos: la clave que se le dio a add-watch, una referencia al átomo, el valor anterior y el nuevo valor.

En el código anterior, usamos la macro lectora #(...) nuevamente para definir una función anónima que imprime los valores antiguo (%3) y nuevo (%4) del átomo.

A diferencia de los validadores, las funciones de observación se llaman después de que el valor ha cambiado y solo se llamarán una vez, ¡sin importar la frecuencia con la que se intercambien! 

Por lo tanto, una función watch puede tener efectos secundarios. Que en el momento en que se llama a la función de observación, es posible que el valor del átomo ya haya cambiado nuevamente, por lo que las funciones de observación siempre deben usar los valores pasados como argumentos y nunca eliminar la referencia del átomo.


martes, 25 de enero de 2022

Concurrencia en Clojure - parte 4


Seguimos con concurrencia en Clojure

Las estructuras de datos persistentes son invaluables para la programación concurrente porque una vez que un subproceso tiene una referencia a una estructura de datos, no verá cambios realizados por ningún otro subproceso. Las estructuras de datos persistentes separan la identidad del estado. ¿Cuál es el nivel de combustible en su automóvil? En este momento, podría estar medio lleno. Algún tiempo después estará casi vacío, y unos minutos después de eso (después de que te detengas para llenar) estará lleno. La identidad "nivel de combustible en su automóvil" es una cosa, cuyo estado cambia constantemente. El “nivel de combustible en su automóvil” es en realidad una secuencia de diferentes valores: el 23/02/2012 a las 12:03 era 0,53; en 2012-02-23 14:30 era 0.12; y en 2012-02-23 14:31 era 1.00.

Una variable en un lenguaje imperativo completa (entrelaza, interconecta) la identidad y el estado: una única identidad solo puede tener un único valor, lo que hace que sea fácil perder de vista el hecho de que el estado es realmente una secuencia de valores a lo largo del tiempo. Las estructuras de datos persistentes separan la identidad del estado: si recuperamos el estado actual asociado con una identidad, ese estado es inmutable e muatble, sin importar lo que suceda con la identidad de la que recuperamos en el futuro.

Heráclito lo expresó así: No podías meterte dos veces en el mismo río; porque otras aguas están siempre fluyendo sobre ti. 

La mayoría de los lenguajes se aferran a la falacia de que el río es una sola entidad consistente; Clojure reconoce que está en constante cambio.

Debido a que Clojure es funcional, los átomos pueden no tener bloqueo; internamente, utilizan el método compareAndSet() en java.util.concurrent.AtomicReference. Eso significa que son muy rápidos y no se bloquean (por lo que no hay peligro de interbloqueo). Pero también significa ese intercambio necesita manejar el caso en el que el valor del átomo ha sido cambiado por otro hilo entre llamar a la función para generar un nuevo valor e intentar cambiar ese valor.

Si eso sucede, volverá a intentarlo. Descartará el valor devuelto por la función y lo llamará de nuevo con el nuevo valor del átomo. Ya vimos algo muy similar a esto cuando usamos ConcurrentHashMap. Esto significa que es esencial que la función pase a swap! no tiene efectos secundarios; si los tuviera, entonces esos efectos secundarios podrían ocurrir más de una vez. Afortunadamente, aquí es donde la naturaleza funcional de Clojure vale la pena: el código funcional naturalmente no tiene efectos secundarios.

lunes, 24 de enero de 2022

Concurrencia en Clojure - parte 3


Seguimos con concurrencia en Clojure

Las estructuras de datos persistentes, no tiene nada que ver con la persistencia en el disco o dentro de una base de datos. En cambio, se refiere a una estructura de datos que siempre conserva su versión anterior cuando se modifica, lo que permite que el código tenga una vista consistente de los datos ante modificaciones. 

Podemos ver esto fácilmente en el REPL:

user=> (def mapv1 {:name "paul" :age 45})

#'user/mapv1

user=> (def mapv2 (assoc mapv1 :sex :male))

#'user/mapv2

user=> mapv1

{:age 45, :name "paul"}

user=> mapv2

{:age 45, :name "paul", :sex :male}


Las estructuras de datos persistentes se comportan como si se hiciera una copia completa cada vez que se modifican. Si esa fuera la forma en que se implementaron realmente, sería muy ineficiente y, por lo tanto, de uso limitado 

Afortunadamente, la implementación es mucho más inteligente que eso y hace uso de la estructura compartida.

La estructura de datos persistentes más fácil de entender es la lista. Aquí hay una lista simple:

user=> (def listv1 (list 1 2 3))

#'user/listv1

user=> listv1

(1 2 3)


Y aquí hay un diagrama de cómo se ve en la memoria:



Ahora vamos a crear una versión modificada con cons, que devuelve una copia de la lista con un elemento agregado al frente:

user=> (def listv2 (cons 4 listv1))

#'user/listv2

user=> listv2

(4 1 2 3)

La nueva lista puede compartir toda la lista anterior, sin necesidad de copiar:

Finalmente, creemos otra versión modificada:

user=> (def listv3 (cons 5 (rest listv1)))

#'user/listv3

user=> listv3

(5 2 3)




En este caso, la nueva lista solo hace uso de una parte del original, pero aún no es necesaria la copia.

No siempre podemos evitar copiar. Las listas solo manejan bien las colas comunes: si queremos tener dos listas con colas diferentes, no tenemos más remedio que copiar.

Aquí hay un ejemplo:

user=> (def listv1 (list 1 2 3 4))
#'user/listv1
user=> (def listv2 (take 2 listv1))
#'user/listv2
user=> listv2
(1 2)

Todas las colecciones de Clojure son persistentes. Los vectores, mapas y conjuntos persistentes son más complejos de implementar que las listas, pero para nuestros propósitos todo lo que necesitamos saber es que comparten estructura y que proporcionan límites de rendimiento similares a sus equivalentes no persistentes en lenguajes como Ruby y Java.


Concurrencia en Clojure - parte 2


Seguimos con concurrencia en Clojure

Veamos un ejemplo que muestra cómo las estructuras de datos persistentes de Clojure significan que el estado mutable no puede escapar como lo hace en Java.

(def players (atom ()))

(defn list-players []

 (response (json/encode @players)))


(defn create-player [player-name]

  (swap! players conj player-name)

  (status (response "") 201))


(defroutes app-routes
  (GET "/players" [] (list-players))
  (PUT "/players/:player-name" [player-name] (create-player player-name)))

(defn -main [& args]
  (run-jetty (site app-routes) {:port 3000}))

Esto define un par de rutas: una solicitud GET a /players recuperará una lista de los jugadores actuales (en formato JSON) y una solicitud PUT a "/players/:player-name" agregará un jugador a esa lista. El servidor Embedded Jetty es multiproceso, por lo que nuestro código deberá ser seguro para subprocesos.

Veámoslo en acción. Podemos ejecutarlo desde la línea de comandos con curl:

$ curl localhost:3000/players

[]

$ curl -X put localhost:3000/players/john

$ curl localhost:3000/players

["john"]

$ curl -X put localhost:3000/players/paul

$ curl -X put localhost:3000/players/george

$ curl -X put localhost:3000/players/ringo

$ curl localhost:3000/players

["ringo","george","paul","john"]

Ahora veamos cómo funciona este código. El átomo de los jugadores :
  (def players (atom ()))
 se inicializa en la lista vacía (). Se agrega un nuevo jugador a la lista con conj :
  (swap! players conj player-name)
, y se devuelve una respuesta vacía con un estado HTTP 201 (creado). La lista de jugadores se devuelve mediante la codificación JSON del resultado de obtener el valor de los jugadores con @:
 (response (json/encode @players)))

Todo esto parece muy simple (y lo es), pero algo podría estar preocupándote algo al respecto. Tanto las funciones de listar jugadores como las de crear jugadores acceden a los jugadores. ¿Por qué este código no sufre el mismo problema que el código Java? ¿Qué sucede si un subproceso agrega una entrada a la lista de reproductores mientras otro lo itera y lo convierte a JSON? Este código es seguro para subprocesos porque las estructuras de datos de Clojure son persistentes.

Y que significa que sea persistente? y en el próximo post te cuento. 

sábado, 8 de enero de 2022

Concurrencia en Clojure


Clojure proporciona un estilo híbrido de programación funcional y estado mutable para resolver problemas de forma concurrente; el "Clojure Way" aprovecha las fortalezas de ambos para brindar un enfoque particularmente poderoso para la programación concurrente.

Si bien la programación funcional funciona increíblemente bien para algunos problemas, algunos tienen la modificación del estado como un elemento fundamental de la solución. Aunque puede ser posible crear una solución funcional a tales problemas, es más fácil pensar en ellos de una manera más tradicional. 

Un lenguaje funcional puro no proporciona soporte para datos mutables en absoluto. Clojure, por el contrario, es impuro: proporciona varios tipos diferentes de variables mutables conscientes de la concurrencia, cada una de las cuales es adecuada para diferentes casos de uso. Estos, en conjunto con las estructuras de datos persistentes de Clojure (vamos a cubriremos lo que significa persistente en este contexto más adelante) nos permiten evitar muchos de los problemas que tradicionalmente afligen a los programas concurrentes con estado mutable compartido.

La diferencia entre un lenguaje funcional impuro y un lenguaje imperativo es de énfasis. En un lenguaje imperativo, las variables son mutables por defecto y el código idiomático las modifica con frecuencia. En un lenguaje funcional impuro, las variables son inmutables por defecto y el código idiomático modifica aquellas que no lo son solo cuando es absolutamente necesario. Como veremos, las variables mutables de Clojure nos permiten manejar los efectos secundarios del mundo real sin dejar de ser seguros y consistentes.

Veremos cómo las variables mutables de Clojure funcionan en conjunto con las estructuras de datos persistentes para separar la identidad del estado. Esto permite que varios subprocesos accedan a variables mutables simultáneamente sin bloqueos (y el peligro asociado de interbloqueo) y sin ninguno de los problemas de estado mutable escapado u oculto. Comenzaremos por lo que podría decirse que es el más simple de los tipos de variables mutables de Clojure, el átomo.

Un átomo es una variable atómica  (los átomos de Clojure están construidos sobre java.util.concurrent.atomic). A continuación, se muestra un ejemplo de cómo crear y recuperar el valor de un átomo:

user=> (def my-atom (atom 42))

#'user/my-atom

user=> (deref my-atom)

42

user=> @my-atom

42

Un átomo se crea con átomo, que toma un valor inicial. Podemos encontrar el valor actual de un átomo con deref o @.
Si desea actualizar un átomo a un nuevo valor, se usa swap !:

user=> (swap! my-atom inc)
43
user=> @my-atom
43

Esto toma una función y le pasa el valor actual del átomo. El nuevo valor del átomo se convierte en el valor de retorno de la función. También podemos pasar argumentos adicionales a la función, como en este ejemplo:

user=> (swap! my-atom + 2)
45

El primer argumento pasado a la función será el valor actual del átomo, y luego cualquier argumento adicional dado para intercambiar. Entonces, en este caso, el nuevo valor se convierte en el resultado de (+ 43 2).

En raras ocasiones, es posible que desee establecer un átomo en un valor que no dependa de su valor actual, en cuyo caso puede usar reset !:

user=> (reset! my-atom 0)
0
user=> @my-atom
0

Los átomos pueden ser de cualquier tipo; muchas aplicaciones web utilizan un mapa atómico para almacenar datos de sesión, como en este ejemplo:

user=> (def session (atom {}))
#'user/session
user=> (swap! session assoc :username "paul")
{:username "paul"}
user=> (swap! session assoc :session-id 1234)
{:session-id 1234, :username "paul"}

Ya se que no vimos nada de concurrencia aun, pero paciencia, como me queda largo el post seguimos en otro... 

miércoles, 15 de septiembre de 2021

ReactiveX


ReactiveX es un framework para los lenguajes de programación más utilizados: Java, JavaScript, C#, Scala, Clojure, C ++, Ruby, Python, Groovy, JRuby, Kotlin, Swift y más. 

RxJava es un framwork que implementa los conceptos de ReactiveX en Java. Veamos un ejemplo RxJava:

List<Integer> input = Arrays.asList(1, 2, 3, 4, 5);

Observable.from(input).filter(new Func1() {

    @Override

     public Boolean call(Integer x) {

          return x % 2 == 0;

     }

})

o usando lambda : 

Observable.from(input).filter(x -> x % 2 == 0);


El objeto resultante (la instancia de rx.Observable) generará una secuencia de los números pares contenidos en la secuencia de entrada: 2 y 4.

En RxJava, rx.Observable agrega dos semánticas al patrón Observador de Gang of Four (la semántica predeterminada es emitir elementos creados, como una lista con elementos 2,4 en el ejemplo anterior):

  • El productor puede notificar al consumidor que no hay más datos disponible.
  • El productor puede notificar al consumidor que ha ocurrido un error.
La biblioteca RxJava proporciona un modelo de programación donde podemos trabajar con eventos generados desde UI o llamadas asincrónicas de la misma manera en que operamos con colecciones y streams en Java 8.

La biblioteca RxJava se creó en Netflix como una alternativa más inteligente a Java Futures y devoluciones de llamada. Tanto los futuros como las devoluciones de llamada son fáciles de usar cuando solo hay un nivel de ejecución asincrónica, pero son difíciles de administrar cuando están anidados.

El siguiente ejemplo muestra cómo se maneja el problema de las devoluciones de llamada anidadas en RxJava.

Suponga que necesita llamar a una API remota para autenticar a un usuario, luego a otra para obtener los datos del usuario y a otra API para obtener los contactos de un usuario. Normalmente, tendría que escribir llamadas a API anidadas y hacer complejos callbacks. Pero con RxJava se puede hacer así : 

serviceEndpoint.login().doOnNext(accessToken -> storeCredentials(accessToken)).flatMap(accessToken -> serviceEndpoint.getUser()).flatMap(user -> serviceEndpoint.getUserContact(user.getId()))




lunes, 3 de mayo de 2021

Como correr los test proyecto clojure?


Si vieron este post : https://emanuelpeg.blogspot.com/2021/04/como-crear-un-proyecto-clojure.html

Deben haber pensado: y los test, donde están los test?

Bueno, vamos a hacer una pequeña función cuadrado por ejemplo : 

(defn cuadrado [n] (* n n))

Y vamos a hacer un test : 

(ns recfun.core-test
(:require [clojure.test :refer :all]
[recfun.core :refer :all]))

(deftest a-test-of-cuadrado
(testing "2 * 2 = 4 "
(is (= (cuadrado 2) 4))))

Y corremos los test con :

$ lein test

lein test recfun.core-test

Ran 1 tests containing 1 assertions.
0 failures, 0 errors.

Y listo, voy a hacer un test que termine mal, así se ve : 

(deftest other-test-of-cuadrado
(testing "2 * 2 = 5 (solo para que termine mal) "
(is (= (cuadrado 2) 5))))

Y termina mal :

$ lein test

lein test recfun.core-test

lein test :only recfun.core-test/other-test-of-cuadrado

FAIL in (other-test-of-cuadrado) (core_test.clj:11)
2 * 2 = 5 (solo para que termine mal) 
expected: (= (cuadrado 2) 5)
  actual: (not (= 4 5))

Ran 2 tests containing 2 assertions.
1 failures, 0 errors.
Tests failed.

jueves, 29 de abril de 2021

Como crear un proyecto clojure?

 Esto es muy fácil hacemos así : 


$ lein new app nombreDeLaApp

Generating a project called nombreDeLaApp based on the 'app' template.


Como ustedes pueden ver utilice el template app, pero que es eso de los templates? Bueno, leiningen provee un conjunto de template que permiten hacer proyectos con una estructura determinada. 

Y si quieren ver los template acá dejo el link : https://clj-templates.com/

Y listo! Ahora si queremos correr la app hacemos : 

$ cd nombreDeLaApp
$ lein run

Y listo!!!!