Publicidad:
La Coctelera

un rato de sol

i will work harder, ja ja ja, no ahora en serio.

30 Noviembre 2008

relaciones familiares

Ruby tiene, desde mi punto de vista, muchas cosas buenas y tan sólo algunas malas. Casi todas las buenas tienen su origen en los lenguajes de programación que más influyeron a Matz cuando diseño Ruby: Lisp y Smalltalk, al igual que las malas (*cough*, Perl *cough*).

Es curioso comprobar hasta que punto Smalltalk ha influido en Ruby y que mejor forma de hacerlo que con un pequeño juego: echar un vistazo rápido al código necesario para hacer las mismas cosas en uno y otro lenguaje, sacadas del tutorial de GNU Smalltalk:

Representando un objeto como una cadena de texto

 rb> "Hola ruby".to_s
 
 st> "Hola Smalltalk" printNl
 

Aritmética

 rb> 9.+ 7
 
 st> 9 + 7
 

Como en Smalltalk los mensajes se mandan a los objetos sin usar la sintaxis del operador ".", la notación de la aritmética es ortogonal con el resto del mecanismo de paso de mensajes. Ruby ofrece una sintáxis "alternativa" además del envío normal de un mensaje como se muestra en el ejemplo.

Colecciones

 rb> x = Array.new
 rb> x.at 0
 rb> x.insert(0,99)
 rb> x = Set.new
 rb> x.add(5).add(7).add('foo')
 rb> x.delete 5
 
 st> x := Array new: 20
 st> x at: 1
 st> x at: 1 put: 99
 st> x := Set new
 st> x add: 5; add: 7; add: 'foo'
 st> x remove: 5
 

Sí amigos, Smalltalk no es zero indexed: "no C allowed in Smalltalk land". Es interesante observar como la elegancia de la sintaxis de Smalltalk, permite enviar mensajes con más de un argumento escribiendo un código muy legible.

 rb> x = { 'a' -> 1, 'b' -> 2}
 
 st> x := Dictionary new. x at: 'a' put: 1; at: 'b' put: 2
 

Un ejemplo muy representativo: donde Ruby introduce pragmáticamente un poco de Perl para simplificar la creación de hashes, Smalltalk prefiere mantener la regularidad en la sintaxis a pesar de que ello signifique tener que escribir más código.

Bloques

 rb> 1.upto(20).each{|i| puts i}
 rb> x.each_pair { |key,value|
              Kernel.puts "#{key} - #{value}"
            }
 
 st> 1 to: 20 do: [:i| i printNl ]
 st> x keysAndValuesDo: [ :key :value |
             key print
             ' - ' print.
             value printNl.
           ]
 

Quizá una de las deudas más visibles que tiene Ruby con Smalltalk. El uso de los bloques es tan similar, que cuando escribes código Ruby que usa bloques estás directamente escribiendo código Smalltalk.

 rb> if (1 < 2)
 rb>   puts "menor"
 rb> else
 rb>   puts "mayor"
 rb> end
 
 st> (1<2) ifTrue: [ 'menor' printNl ] ifFalse: [ 'mayor' printNl ]
 

De hecho el uso de bloques en Smalltalk se lleva hasta sus últimas consecuencias. Donde Ruby introduce operadores especiales para las condicionales, Smalltalk sigue fiel a la visión de "todo es un objeto" y no hace más que pasar bloques como argumento de los mensajes ifTrue: e ifFalse: a un objeto Boolean. Regularidad, regularidad, regularidad...

Clase Object

 rb> Object.name
 st> Object name
 

Nuestra vieja y querida clase Object sigue siendo la raíz de la jerarquía de clases, como no podía ser de otra manera.

Definiendo una clase y variables de instancias (no accesibles desde el exterior)

 rb> class Account < Object; end
 rb> Account.instance_variable_set :@balance, null
 
 st> Object subclass: #Account
 st> Account instanceVariableNames: 'balance'
 

Una pequeña diferencia aparece en la creación de clases. Ruby introduce una sintaxis especial para declarar una clase y para especificar la herencia. Smalltalk sin embargo, sigue siendo completamente ortogonal con el resto de su diseño: para crear una subclase se manda un mensaje subclass: al objeto clase Object con el nombre de la nueva clase. Idem, para definir una nueva variable de instancia. Aquí si que Ruby es fiel al paradigma de paso de mensajes si el programador lo desea (excepto por la traza de Perl en forma de '@'). En ambos lenguajes la encapsulación es completa, y no es posible acceder a las variables del objeto, excepto usando mecanismos de introspección.

Implementando una clase

 rb> class Account < Object
 rb>   def initialize
 rb>     @balance = 0
 rb>   end
 rb>   def spend(amount)
 rb>     @balance -= amount
 rb>     return self
 rb>   end
 rb>   def deposit(amount)
 rb>     @balance += amount
 rb>     return self
 rb>   end
 rb>   def balance
 rb>     @balance
 rb>   end
 rb> end
 
 st> Object subclass: Account [
 st>   | balance |
 st> ]
 st> Account class extend [
 st>   new [ | r |
 st>         r := super new.
 st>         r init.
 st>         ^r
 st>   ]
 st> ]
 st> Account extend [
 st>   init [
 st>     balance := 0
 st>   ]
 st>   spend: amount [
 st>     balance := balance - amount
 st>   ]
 st>   deposit: amount [
 st>     balance := balance + amount
 st>   ]
 st>   balance [
 st>     ^balance
 st>   ]
 st> ]
 

La implementación completa de una clase se realiza de una forma muy similar en ambos lenguajes. En Smalltalk es necesario implementar el método new: a nivel de clase y en ese método invocar al método init: a nivel de instancia para iniciar el estado del nuevo objeto. En Ruby sólo es necesario implementar el método initialize, ya que new, ya está definido en Class.

Metamodelo

 st> x = Set.new
 st> x.class
   Set
 
 st> x := Set new
 st> x class
   Set
 
 rb> Set.superclass
   Object
 
 st> Set superclass
   HashedCollection
 st> Set superclass superclass superclass superclass superclass
   Object
 
 rb> Object.superclass
   nil
 
 st> Object superclass
   nil
 
 rb> Set.class
   Class
 
 st> Set class
   Set class
 
 rb> Set.class.superclass
   Module
 rb> Set.class.superclass.superclass
   Object
 
 st> Set class superclass superclass superclass superclass superclass superclass
   Object
 
 rb> Set.class
   Class
 rb> Class.class
   Class
 
 st> Set class
   Set class
 st>  Set class class
   Metaclass
 

Esta es la característica más importante de Smalltalk, la presencia de un metamodelo accesible y modificable del lenguaje subyacente, que Ruby ha heredado y que mantiene una gran similitud con el original de Smalltalk, a pesar de las pequeñas diferencias (en Smalltalk por ejemplo, la Metaclase es explícita cuando en Ruby es la clase Class).

Como vemos, muchas de las grandes innovaciones que introdujo Smalltalk en su día, han llegado intactas hasta Ruby, tanto, que a veces Ruby parece un dialecto de Smalltalk. Ruby ha aportado un mayor pragmatismo y orientación hacia el scripting, y un mejor soporte del paradigma funcional: devolviendo al última sentencia evaluada por ejemplo, frente a Smalltalk que siempre devuelve el objeto al que se le ha enviado el mensaje por defecto. Sin embargo, existen características de Smalltalk que lamentablemente, se quedaron por el camino y que habrían hecho de Ruby un lenguaje mucho más interesante, como el concepto de imagen de ejecución del lenguaje.

Tags: smalltalk

servido por Antonio sin comentarios compártelo

sin comentarios · Escribe aquí tu comentario

Escribe tu comentario


Sobre mí

Avatar de Antonio

un rato de sol

Barcelona/Salamanca, España
ver perfil »
contacto »
Trabajador del metal y del acero, en la gloriosa XING AG, escribo software con el que poder ganarme el jornal. En mi tiempo libre sigo tecleando código de bonitos colores a medio camino entre lo sublime y lo terrible. Últimamente me gustan mucho los gatos.

Fotos

Antonio Garrote Hernández todavía no ha subido ninguna foto.

¡Anímale a hacerlo!

Buscar

suscríbete

Selecciona el agregador que utilices para suscribirte a este blog (también puedes obtener la URL de los feeds):

¿Qué es esto?

Crea tu blog gratis en La Coctelera