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.
