Ruby Is Smalltalk Minus Minus

Apparently there's nothing you can do in RubyLanguage that you can't do in SmalltalkLanguage, and there's all these things you can do in Smalltalk, that you can't do in Ruby:

So therefore, we can only conclude that we must stop using Ruby to do whatever it was we were doing, and start doing it in Smalltalk instead.

SmugSmalltalkWeenies? consider just about all of the purported advantages listed in RubyInsteadOfSmalltalk to be disadvantages.


From RichardKulisz

Allow me to further explain why Ruby has many advantages over Smalltalk in the meta-object department. This example is contrived, but directly relates to the above "ActiveRecord" example. It is also a very famous example that I did not author, called Dwemthy's Array (you can find the original information at http://mislav.uniqpath.com/poignant-guide/dwemthy/ ).

When we're making classes, sometimes we realize that we're making something that lots of people are going to subclass a lot. In prototyping languages, we'd say we're making a prototype that will often be copied and modified, like the I/O prototypes. In these cases, it's usually easy to say why people would want to subclass. "Oh, they'd want to play with the buffering" or "Oh, they'd be changing some constants and probably defining some new relations."

How about in a game? We make a Monster class, and in the game Monsters can have lots of different variations on their statistics. What we'd like to be able to do is provide them with a lisp-like meta-language to make monsters quickly. We want the developers to care more about the game than the arcane internals of our complex realtime monster class (now with Uber-AI!)

What we'd like to see, is something like this:

 class Dragon < Creature
life 1340
strength 451  
charisma 1020 
weapon 939
 end

What we're saying is that you could make new Dragons, and they'd have those stats. Our intrepid heroes could battle each dragon individually, and we could spawn many more as needed. But, what if we had something more exotic, like our heroes? Heroes tend to be weaker, but have lots of special moves. Our rabbit-hero has bombs. So we'd want to be able to mention this so our Game's OS will note it, and keep track of it for us:

 class Rabbit < Creature
traits :bombs  

life 10 strength 2 charisma 44 weapon 4 bombs 3

... end

Note that these are simply Ruby classes. You can add methods below (or inbetween) if you see fit. What we've done is built a DomainSpecificLanguage just for gaming. And, we've built it in a totally OO fashion, and without us worrying about what bucket these traits go into. Note how we even used one method, "traits" to extend the class's meta language as we were defining it!

It's this meta-object editing that makes Ruby such an awesome OO language. DomainSpecificLanguages were one of the major draws of Lisp, but it was something that Smalltalk only supported in the most limited of fashions with blocks. Ruby picks up where Smalltalk left off and carries the ball and brings in the touchdown. -- DaveFayram (Where it found Lisp waiting to greet it in the end zone. :) -- DanMuller (Indeed. -- DaveFayram))

I think a few smalltalkers might disagree with that assertion. Smalltalk does DSL's better than Ruby, Smalltalk does it so well that there's no need for special keywords in the language like "if/for/while", Ruby can't make that claim, if Ruby's object system and limited version of blocks are so good, why can't they model predicate logic as messages to objects rather than magic keywords? I'll tell you why, because Ruby can't cleanly pass more than one block to a method, and that seriously limits it's capabilities in comparison with Smalltalk blocks, which are much better. Why have the class keyword, why not do class declaration as a message send to an object.

class Rabbit < Creature end

would be a simple message send in Smalltalk

Creature subclass: #Rabbit

Ruby is elegant in comparison to Java, but it has a long way to go before it can touch Smalltalk, Ruby is still SmalltalkMinusMinus imho.

RamonLeon

--- Challenge to Smalltalk advocates: go ahead and reimplement DwemthysArray? then! The Array is a rather short program so it shouldn't take you too long. It's also rather well known, even celebrated, by now so a ST version will reach many people who wouldn't normally even notice ST advocacy. And an elegant implementation will frankly be a lot more convincing than "sure, we could do that" assertions. We've been promised Smalltalk DWEMTHY before: http://redhanded.hobix.com/inspect/theRabbitWillDieInSmalltalk.html - who will deliver? -- May 2008 update - we now have a contender: http://softwareengineering.vazexqi.com/2008/05/28/dwemthys-array-in-smalltalk.html

If you're desparate to create classes with a message in Ruby, it turns out that the following will give you the ability:

  class Object
def self.subclass name
  eval "class #{name} < #{self}; end"
end
  end
The Smalltalk example above then translates to:
  Creature.subclass :Rabbit
But I'm not sure exactly what this buys you. I guess in ST, the Creature class responds to the subclass message by opening an editor in the environment (don't have an ST env to hand to verify this), but there's no analogue in the Ruby world, so you'd probably be better off declaring classes idiomatically.

--- Actually, there is a nicer way to do this in Ruby then #eval:

  A = Class.new(Object)
  A.class_eval do
#class body as code, not string
  end

This is equivalent to class name < superclass.

Playing with #const_set and #const_get you can mimic the ST-Style quite well. This is a rough implementation for illustration:

  class Object
def self.subclass const_name = nil, &block
c = Class.new(self);
c.class_eval &block
const_name ? self.const_set(const_name, c) : c
end
  end

c = Object.subclass(:Rabbit) def name "Roger" #the only true rabbit... end end

c.name

It is easy to extend this for almost all you needs. --FlorianGilcher?


...because Ruby can't cleanly pass more than one block to a method...

We are to assume this phrase hinges on the word cleanly, thus the following fails as a counterexample?

  def with_blocks  x, y, a, b
a.call x
b.call a, y
  end

with_blocks :x, :y, proc { |x| puts "a: #{x}" }, proc { |a, y| puts "b: #{y}"; a.call y }

Yeah, that really is quite ugly. I can tell that much even without being able to tell what it does. Could you provide a Smalltalk version here?

 "Define the method"

SomeObject>>doSomethingUsingBlock: oneBlock andAnotherBlock: anotherBlock oneBlock value. anotherBlock value.

"Call the method (on an instance of SomeObject)"

aSomeObject doSomethingUsingBlock: [self foobar] andAnotherBlock: [self zeetix]

 True>>ifTrue: trueAlternativeBlock ifFalse: falseAlternativeBlock
^trueAlternativeBlock value

False>>ifTrue: trueAlternativeBlock ifFalse: falseAlternativeBlock ^falseAlternativeBlock value

"Calling ..." aBoolean ifTrue:[Transcript show: 'True'] ifFalse: [Transcript show: 'False']

-- TomStambaugh


I can get pretty close to the ST example...

  class Bool
def initialize cond
  @cond = cond
end

def if_true &block if @cond block.call end self end

def if_false &block if !@cond block.call end self end end

b = Bool.new(true)

b.if_true { puts 'true'}.if_false { puts 'false' }
Maybe we don't need to pass more than one block into a function after all...

This doesn't match the Smalltalk example because you're throwing away the return values of the procs:

  myValue := aBool ifTrue: [1] ifFalse: [2]

-- FrankShearar


OK, here's a stab at your SomeObject example. Mine doesn't have a zeetix method, though - I'll leave that as an exercise for the reader (I just threw in a block for variety). This example is leveraging a hash to do most of the work though, and the end result ends up looking somewhat like Perl! I guess the Smalltalk example is freer of syntactic noise, although I'm not immediately clear where 'value' is passed in in the Smalltalk.

  class SomeObject
def with_blocks args
  args[:some_block].call args[:value]
  args[:another_block].call args[:value]
end

def foobar args result = "" args.each { |a| result += "#{a} " } puts "foobar: #{result}" end end

obj = SomeObject.new

obj.with_blocks({ :some_block => obj.method(:foobar), :another_block => proc { |args| puts "#{args.size} things." }, :value => [1, 2, 3, 4, 'Jon'] })


Consider the following:

  def make_class name
eval "class #{name}; end"
  end

How would you go about doing the same thing in Smalltalk? (Please don't infer that that above code is in any way useful, I'm just intregued as to what the equivelent Smalltalk would look like...)

I'm guessing that's a method that takes one argument, a string, and creates a class of that name.

Broadly yes - it's a method that takes an argument which is either a String, or an object which responds to to_s with a String. But I am being pedantic, so please ignore me.

subclass: aString

^self subclass: aString asSymbol

instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''.

subclass:instance... is the method the browser uses to create classes.

Now, if you want it to produce a string including ST code which the compiler gets to process then,

subclass: aString

^('^self subclass: ', aString asSymbol printString, '

instanceVariableNames: 
classVariableNames: 
poolDictionaries: .') evaluate

<shudder> Actually, you'd probably use expandMacros somehow.

The first example I'm guessing is closer to the SmalltalkWay?, and the first 2 lines make sense to me. But what's all this instanceVariablenames, classVariableNames, poolDictionaries gubbins? Are they strictly necessary? Would:

 subclass: aString
 ^super subclass: aString asSymbol.

not work? And be closer to the ruby example? It would certainly seem more elegant than my ruby example (unless there is a more RubyWay to do it).

There's no message "subclass:" because nobody in the Smalltalk world ever, but ever, makes subclasses in code. They make subclasses in the IDE and even the lamest browser will provide you with a template that you can just edit and accept. You never have to bother with classVariables and poolDictionaries unless you know what you're doing, and instance variables are easily specified. In the case of Dolphin, the browser provides you with a template that would recreate the class you've currently got selected if you aren't examining a method in that class.

Can you provide some concrete examples, aside from the Creature example above, where you'd programmatically create classes? Because I've yet to encounter a single one so it seems strange to hear you say it's done "quite frequently". -- RK

Well, that explains how it's done. Which is not exactly, or at all frankly, what I asked for since I already knew that Smalltalk and Self could implement each other and the dynamic template pattern is just Smalltalk emulating Self (and ugly it is too). Now, can you provide some actual examples of anyone using it? Say, from your own working experience? -- RK


Here's an (almost) RealWorld example which I think shows one of Ruby's strengths. It's a messaging system. There are three main components, an Address class, a Listener module and a Dispatcher module.

The Address class is as follows, each address holds a path object, an array of child Addresses, and an array of Listeners. The path value is interpreted as a string of the form '/a/b' where adding the Address '/a/b' would also create '/a' if it didn't exist, and add '/a/b' as a child of '/a' (and '/a' as a child of '/'). This is a skeleton (i've stripped out most of the implementation, as it's not essential to my point):

  class Address
def self.get path
  #return address object for path or nil
end

def self.add path #register an address object for path and return it end

def add_child child @child.push child end

def add_listener listener @listener.push listener end

def each_listener @listener.each { |l| yield l } end

def each yield self each_child { |c| yield c } end

def each_child @child.each { |c0| c0.each { |c1| yield c1 } } end

end

Now, as the Address class is doing most of the work, the two modules are pretty simple:

  module Listener
def listen addr
  a = Address.add addr
  if a
a.add_listener self
  end
  nil
end
  end

module Dispatcher def broadcast addr, *args a0 = Address.get addr if a0 a0.each { |a1| a1.each_listener { |l| if l != self l.receive *args end } } end nil end end

now, if you want to create an object which can both listen for messages and dispatch them, you'd need something like the following (this is why I implemented Listener and Dispatcher as modules rather than classes):

  class Test
include Listener
include Dispatcher

def receive *args #handle message (*args) here end end

Two includes, and one method implementation. That's it. My object can now listen and broadcast as it pleases.

  t = Test.new

t.listen '/some/address' t.boadcast '/', :some_message

Maybe I'm easily impressed, but I think that's pretty elegant.

Yes, mixins are Ruby's coolest feature, good enough that Smalltalk has already stolen the concept, improved upon it, and called em Traits. Traits are mixins, without the dependencies on the order of inclusion that Ruby's mixins have, so they are more well behaved. It's also a good example of why Smalltalk is superior, there's not much you could think up that you couldn't simply add to Smalltalk, implemented in Smalltalk. If Ruby didn't have traits, I don't think you could add traits by hacking a litte Ruby, especially since much of Ruby is implemented not in Ruby, but in C. Smalltalk is a language that can grow into anything you need it to be, to an extent far greater than Ruby. Ruby looks cool as hell, if you're coming from Java or CSharp or some such popular language, but if you look at Ruby from a Smalltalk or Lisp perspective, it's a step down.

Traits/mixins could be implemented in pure Ruby easily enough. Ruby, like Smalltalk, provides sufficient runtime reflection and metaprogramming capabilities to perform namespace manipulation without resorting to C extensions. It's the reflection and metaprogramming stuff that makes Smalltalk so flexible. The implementation language of the VM is a relatively minor detail.

That's not saying Ruby is bad or anything, but if you like Ruby, or find it impressive, then take another step up, and try Smalltalk, you'll like it better, especially when you realize how poor Ruby's tools are in comparison. If you think Ruby is impressive, then Smalltalk will blow your mind, hell, Ruby still uses files, how object oriented is that? Live in a Smalltalk image for a while, you'll realize how primitive files are, you'll realize how awesome it is to live in a world of pure living objects in a virtual environment, where you can prototype freely with no need of a database to store your objects. Where you can change a running program, change the implementation of a class while looking at a live instance of it. Ruby on rails is only impressive if you still think you need a database for rapid prototypes. Ruby is a great language, but it's not in Smalltalk's league, not yet. Smalltalk's had 30+ years of some of the most brilliant minds working on it, Ruby's a new kid on the block, it needs time to mature before it's ready to take on Smalltalk.

It *is* awesome to live in a world of pure objects, but that's not the world most of us live in. The Smalltalk image is fantastic, but Ruby's traditional file-based approach lends it better integration with the rest of the world. It's a tradeoff, and I think the Ruby developers made the right choice given the application domain and environments for which Ruby is intended.

"better integration with the rest of the world" does not make something better, the rest of the world is wrong, image based development "is" better, one day the world will come to Smalltalk, it's been slowly creeping towards Smalltalk for years, Ruby is proof of that, it's Smalltalk, minus some of the better features of Smalltalk.

My hypothesis would be that if the rest of the world is going to end up using Smalltalk, it's probably precisely because of languages such as Ruby that they'll find the jump to Smalltalk that little bit easier than it would have otherwise been.

I agree completely.

In my view, there is an emerging world of totally cool things that tools like Smalltalk, Lisp, perhaps Ruby (I'm unfamiliar with it, and so rely on the opinions of others), and similar languages/environments let me experience more directly. I can get there with Perl and, to a lessor extent, Java, but there's a lot more noise. Python impresses me. The tool is not the point, though, it's the cool things revealed by the tool. I gladly use anything that lets me live and work in the new world. For too long, Smalltalk was the only game in town. Now, thankfully, Javascript and Python let me get there as well and don't face the market resistance that continues to plague Smalltalk. It doesn't matter to me how the rest of the programming community gets there or what language they use. Many of these concepts are difficult, their implications subtle and surprising, and I find that my experience in working with them for many years (even in Smalltalk) helps a great deal with those new developers just getting to them through Ruby or Python. To the newcomers, I say "welcome aboard" -- and the language doesn't matter. -- TomStambaugh


I found this site by asking the very question What does Ruby do that Smalltalk doesn't. (http://www.google.com/search?ie=utf8&oe=utf8&q=what+does+ruby+do+that+smalltalk+doesn%27t). I learnt Smalltalk quite late in my software engineering career and I can see why it's used in education, and also why it has some very vocal supporters. Simply put it has a simplicity and elegance that is unmatched by any language prior or since. Many other languages have borrowed ideas from it. Recently I've had enough time to begin learning Ruby something there is currently a lot of buzz about, I've read enough about it in a couple of days to ask that question. It seems to me that there isn't anything. You see, Smalltalk doesn't have to impress Ruby followers - it came first. To offer anything to one who knows Smalltalk it must improve upon it. Of course anyone moving to either language from say C++ or C# (proof that new does not equal good) will be getting a breath of fresh air.

I love Smalltalk, but back in the real world of software development almost no-one uses it. It is a better language from a purist point of view but now a Cocoa-Ruby bridge is part of Mac OS X development tools real desktop applications can be created in something a little closer to the language I love. Objective-C is damn good, but it isn't Smalltalk. Ruby is a step closer.

So I say that while Smalltalk is closer to an ideal, a pragmatist will choose Ruby.

Aside from that main point I think a possible reason for the Ruby people to not 'get' the image based approach of Smalltalk is that right now Smalltalk VMs exist only inside another host OS, one that is usually file based. It might help to imagine a computer where the operating environment is Smalltalk. Replace the idea of your hard disc with the image. You don't save files, but objects. How liberating! But Smalltalk is not really a language, it's an environment. The language is inseparable from the rest. The parts just don't make sense without each other. So by taking ideas from just the language part and disregarding the rest Ruby will only ever be a side step at best.

--Tim Bedford


If keywords ("special language constructs") are bad, then Ruby is better than Smalltalk in one way: Ruby doesn't require a keyword to return a value from a method:

 def foo
   'foo'
 end

whereas Smalltalk would require ^'foo'.


If at all, then RubyIsSmalltalkMinusMinusPlusPlusMinusMinus because in that way you can probably reach any language from any other. See LanguagePlusPlusMinusMinus.


WikiNow.

See RubyInsteadOfSmalltalk for a contrasting view.

CategoryProgrammingLanguage


EditText of this page (last edited November 8, 2014) or FindPage with title or text search