Publicado Ruby 3.0.0 RC1

Nos complace anunciar la publicación de Ruby 3.0.0-rc1.

Introduce una serie de características nuevas y mejoras en desempeño.

Análisis Estático

RBS

RBS es un lenguaje para describir los tipos de los programas Ruby.

Los verificadores de tipos, incluyendo TypeProf y otras herramientas que soporten RBS entenderán mejor los programas Ruby con definiciones RBS.

Usted puede escribir la definición de clases y módulos: métodos que se definen en la clase, variables de instancia y sus tipos, y relaciones herencia/mix-in.

El objetivo de RBS es soportar los patrones que comúnmente se ven en programas en Ruby y permitir escribir tipos avanzados incluyendo tipos unión, sobrecarga de métodos y genéricos. También soporta tipado pato (duck typing) con tipos de interfaz.

Ruby 3.0 se distribuye con la gema rbs, que permite analizar y procesar definiciones de tipos escritas en RBS. El siguiente es un pequeño ejemplo de RBS con una clase, un modulo y definiciones de constantes.

module AplicacionMensajeria
  VERSION: String
  class Channel
    attr_reader nombre: String
    attr_reader mensajes: Array[Mensaje]
    attr_reader usuarios: Array[Usuario | Robot]           # `|` significa tipos unión, `Usuario` o `Robot`.

    def initialize: (String) -> void

    def publicar: (String, de: Usuario | Robot) -> Mensaje # Se soporta sobrecarga de métodos.
            | (File, de: Usuaurio | Robot) -> Mensaje
  end
end

Ver más detalles en el archivo README de la gema rbs.

TypeProf

TypeProf es una herramienta para análisis de tipos incluida en el paquete Ruby.

Actualmente, TypeProf sirve como una forma de inferencia de tipos.

Lee código Ruby plano (sin anotiaciones de tipos), analiza que métodos se definen y como se usan, y genera un prototipo de la firma de los tipos en formato RBS.

Aquí hay una simple demostración de TypeProf.

Entrada de ejemplo:

# test.rb
class User
  def initialize(name:, age:)
    @name, @age = name, age
  end
  attr_reader :name, :age
end
User.new(name: "John", age: 20)

Salida de ejemplo:

$ typeprof test.rb
# Classes
class User
  attr_reader name : String
  attr_reader age : Integer
  def initialize : (name: String, age: Integer) -> [String, Integer]
end

Puede ejecutar TypeProf guardando el archivo de entrada como “test.rb” y ejecutandolo como “typeprof test.rb”.

También puede probar TypeProf en línea. (TypeProf corre al lado del servidor, así que ¡disculpe si no está operando!)

Ver detalles en la documentación y en demostraciones.

TypeProf es experimental y aún no es una herramienta madura, sólo soporta un subconjunto del lenguaje Ruby, y la detección de errores en tipos es limitada. Pero está creciendo rapidamente para mejorar la cobertura de las características del lenguaje, el desempeño del análisis y la usabilidad. Toda retroalimentación es bienvenida.

Ractor (experimental)

Un Ractor es una abstracción de concurrencia al estilo Actor-modelo, diseñada para brindar una forma de ejecución en paralelo sin preocuparse por la seguridad de los hilos (thread-safe) de ejecución.

Puede crear múltiples ractors y puede ejecutarlos en paralelo. Un Ractor permite hacer programas paralelos con seguridad en los hilos de ejecución porque los ractors no comparten objetos normales. La comunicación entre ractors se soporta mediante envío de mensajes.

Para limitar los objetos que se comparten, Ractor introduce diversas restricciones a la sintaxis de Ruby (no hay cambio cuando no hay múltiples Ractors).

La especificación e implementación no es madura y podría cambiar a futuro, por eso esta característica se señala como experimental y con el primer Ractor.new se presenta una advertencia de característica experimental.

El siguiente programita calcula prime? en paralelo con dos ractores. Podrá comprobar que la ejecución es casi 2 veces más rápida que la del programa secuencial en un computador paralelo.

require 'prime'

# n.prime? con enteros enviados en r1, r2 que corren en paralelo
r1, r2 = *(1..2).map do
  Ractor.new do
    n = Ractor.receive
    n.prime?
  end
end

# enviar parámetros
r1.send 2**61 - 1
r2.send 2**61 + 15

# esperar resultados de expr1 y expr2
p r1.take #=> true
p r2.take #=> true

Ver más detalles en doc/ractor.md.

Planificador (Scheduler) de Fibras

Se introduce Fiber#scheduler para interceptar operaciones que bloquean. Esto permite una concurrencia liviana sin cambiar el código existente. Dar un vistazo general y ver como funciona en “Don’t Wait For Me, Scalable Concurrency for Ruby 3”.

Los métodos y clases que se soportan en el momento son:

  • Mutex#lock, Mutex#unlock, Mutex#sleep
  • ConditionVariable#wait
  • Queue#pop, SizedQueue#push
  • Thread#join
  • Kernel#sleep
  • Process.wait
  • IO#wait, IO#read, IO#write y métodos relacionados (e.g. #wait_readable, #gets, #puts y así sucesivamente).
  • IO#select no es soportado.

El actual punto de entrada para la concurrencia es Fiber.schedule{...} sin embargo está sujeto a cambios para cuando se publique Ruby 3.

(Explicar la gema Async con enlaces). Este programa de ejemplo realizará varias peticiones HTTP de manera concurrente: (Explicar esto:)

  1. async es una gema exterior
  2. async usa esta nueva característica
require 'async'
require 'net/http'
require 'uri'
Async do
  ["ruby", "python", "c"].each do |topic|
    Async do
      Net::HTTP.get(URI "https://www.google.com/search?q=#{topic}")
    end
  end
end

Otras características notables

  • El reconocimiento de patrones en una línea se ha rediseñado (experimental)

    • se añade =>. Puede usarse como una asignación al lado derecho.

      0 => a
      p a #=> 0
      
      {b: 0, c: 1} => {b:}
      p b #=> 0
      
    • in se ha cambiado para retornar true o false.

      # version 3.0
      0 in 1 #=> false
      
      # version 2.7
      0 in 1 #=> raise NoMatchingPatternError
      
  • Se agrega un patrón Encontrar (Find). (experimental)

    case ["a", 1, "b", "c", 2, "d", "e", "f", 3]
    in [*pre, String => x, String => y, *post]
      p pre  #=> ["a", 1]
      p x    #=> "b"
      p y    #=> "c"
      p post #=> [2, "d", "e", "f", 3]
    end
    
  • El reconocimiento de patrones (case/in) ya no es experimental.

  • Se agrega una definición de métodos que no requiere end.

    def cuadrado(x) = x * x
    
  • Hash#except ahora es un método incorporado.

    h = { a: 1, b: 2, c: 3 }
    p h.except(:a) #=> {:b=>2, :c=>3}
    
  • Memory view se agrega como característica experimental

    • Este es un nuevo conjunto de llamados en la API de C para intercambiar áreas de memoria puras entre las librerías que son extensiones, por ejemplo para intercambiar un arreglo numérico con un mapa de bits. Las librerías que son extensiones pueden compartir también los metadatos del área de memoria que constan de la forma, el formato del elemento y así sucesivamente. Usando esta clase de metadatos, las librerías que son extensiones pueden compartir apropiadamente incluso un arreglo multidimensional. Esta nueva característica se diseñó empleando como referencia el protocolo de colchón (buffer ) de Python.

Mejoras en desempeño

  • Se implemetaron muchas mejoras en MJIT. Ver detalles en el archivo NEWS.
  • Pegar código largo en IRB es 53 veces más rápido que con Ruby 2.7.0. Por ejemplo el tiempo requerido para pegar este código de ejemplo pasa de 11.7 segundos a 0.22 segundos.

Otros cambios notables desde 2.7

  • Los argumentos de palabra clave se separan de otros argumentos.
    • En principio, el código que presente una advertencia en Ruby 2.7 no funcionará. Ver detalles en este documento.
    • Por cierto, el re-envío de argumentos ahora soporta argumentos principales.

      def method_missing(meth, ...)
        send(:"do_#{ meth }", ...)
      end
      
  • La característica $SAFE se eliminó por completo; ahora es una variable global normal.

  • El orden de la traza de llamados (backtrace) se había invertido en Ruby 2.5, pero esto se ha revertido. Ahora se comporta como Ruby 2.4; se imprime primero un mensaje de error y el número de línea donde ocurrió la excepción; las funciones que había hecho la llamada se imprimen después.

  • Se actualizaron algunas librerías estándar.
    • RubyGems 3.2.2
    • Bundler 2.2.2
    • IRB 1.2.6
    • Reline 0.1.5
    • Psych 3.2.1
    • JSON 2.4.1
    • BigDecimal 3.0.0
    • CSV 3.1.9
    • Digest 3.0.0
    • Fiddle 1.0.4
    • StringIO 3.0.0
    • StringScanner 3.0.0
  • Las siguientes librerías ya no son gemas distribuidas con Ruby. Instale las gemas correspondientes para usar sus funcionalidades.
    • net-telnet
    • xmlrpc
  • Las siguientes gemas por omisión se volvieron gemas distribuidas con Ruby.
    • rexml
    • rss
  • Los siguientes archivos de stdlib ahora son gemas y se publicaron en rubygems.org.
    • English
    • abbrev
    • base64
    • drb
    • debug
    • digest
    • erb
    • find
    • io-nonblock
    • io-wait
    • net-ftp
    • net-http
    • net-imap
    • net-protocol
    • nkf
    • open-uri
    • optparse
    • pathname
    • pp
    • prettyprint
    • resolv-replace
    • resolv
    • rinda
    • securerandom
    • set
    • shellwords
    • syslog
    • tempfile
    • time
    • tmpdir
    • tsort
    • un
    • weakref
    • win32ole

Ver más detalles en el archivo NEWS o en la bitácora de contribuciones.

Con estos cambios, 3889 archivos cambiados, 195560 inserciones(+), 152740 eliminaciones(-) desde Ruby 2.7.0!

¡Por favor pruebe Ruby 3.0.0-rc1, y denos cualquier retroalimentación!

Descargas

  • https://cache.ruby-lang.org/pub/ruby/3.0/ruby-3.0.0-rc1.tar.gz

    SIZE: 19488885
    SHA1: 34ede2128a90ef3217d9cab9efcdf20fc444f67c
    SHA256: e1270f38b969ce7b124f0a4c217e33eda643f75c7cb20debc62c17535406e37f
    SHA512: 798926db82d27366b39be97556ac5cb322986b96df913c398449bd3ece533e484a3047fe35e7a6241dfbd0f7da803438f5b04b805b33f95c73e3e41d0bb51183
    
  • https://cache.ruby-lang.org/pub/ruby/3.0/ruby-3.0.0-rc1.tar.xz

    SIZE: 14341128
    SHA1: deff34cf67373dca166e9961051b6c4723aaaec6
    SHA256: f1adda082f9291e394d25ed32975abbef90962dc4c8b11130586a0151558e79a
    SHA512: f4f13dbfa1c96088eb3dbfba0cb1fe99f4e17197ee2d4b78fbe16496780797a10daa3f2ff9c38d2d7b316974101eccf45184708ad05491fb49898b3a7cc6d673
    
  • https://cache.ruby-lang.org/pub/ruby/3.0/ruby-3.0.0-rc1.zip

    SIZE: 23902334
    SHA1: e3e20b4d0ec895e579ae416f2b7552c6be3596f7
    SHA256: 25ced95fa544af6a64d348dc5eace008edfda22f55ed1f6ad9f932b344e6196d
    SHA512: c81b3bf7ce582bf39fd7bc1e691d0777ed4cf38ca6b4d54bc9edaef076ae8bcecb6a86ebfd773591f7d8533e772517033c762d35fdc8b05cb4db4488c2bacec2
    

Qué es Ruby

Ruby fue desarrollado inicialmente por Matz (Yukihiro Matsumoto) en 1993, y ahora es desarrollado como código abierto. Corre en muchas plataformas y se usa en todas partes del mundo especialmente para desarrollos web.