Programming

Back to -Tutorials -Sapphire (Ruby In Steel) Tutorials -Programming

Ruby The Smalltalk Way #1 - Fundamentals

A first dip into Smalltalk for Rubyists...

For a background to this series and an overview of the documentation and software which I’ll be using, refer to the Introduction. You will, at the very least, need the free eBook, The Smalltalk/V Tutorial, which can be downloaded from Stéphane Ducasse’s :: Free Online Books. Code samples are provided in the downloadable Zip archive. You can load up chapter1.st into Dolphin Smalltalk and simply mark and evaluate (CTRL+D) blocks of code as you go along. If you are using Ruby In Steel Developer (or Trial) you can load all the Ruby and C# sample files as a single solution, chapter1.sln which also includes some C# samples. If you are using some other editor or IDE, you will need to load the Ruby (.rb) files one by one).


Lesson Plan:

Chapter One of the Smalltalk/V Tutorial. I’ll be covering this in three parts...
- 1) Fundamentals (this article)
- 2) A Question of Style
- 3) The World According To Objects


Zip - 15.1 kb
Source code (Ruby, Smalltalk, C#) for this article

In this article, we’ll take a look at some of the fundamental features of Smalltalk’s Object Orientation Programming (OOP) and see how they relate to Ruby. This will be a fairly lightweight introduction to some of the ideas of Smalltalk and Ruby’s OOP and some basic syntax of those two languages. We’ll start going into more depth on many of these issues when we get to Chapter Three.

Background reading is the first part of Chapter One of the Smalltalk/V manual - in the PDF download, that’s pages 15 to 18 (as shown in the bar at the bottom of the Acrobat Reader) , ending with the section named ‘A Function Call With Three Arguments’.

Smalltalk’s Big Ideas

To understand just how revolutionary the ideas of Smalltalk were, you have to try to put yourself back into the period when it was developed, in the 1970s, at a time when computer screens showed nothing more than green text on a black background and mice were things that lived in skirting-boards and ate cheese. Computer programming, in those days, was a decidedly non-interactive pastime which involved editing-compiling-and-linking (if you were lucky) or punching holes into cards and feeding them into slots (if you weren’t). Most programming languages and environments had no ‘visual design’ capabilities, very little if anything in the way of debugging and certainly no ‘objects’.

And then along comes Smalltalk - with its slick graphic environment, its ability to ‘mark and execute’ code instantly, its overlapping windows, bitmapped fonts, context sensitive menus, mouse control and, as an added bonus, a thing called Object Orientation (to name just a few of its big ideas). The rest of the computing world took another decade even to begin to catch up. And today, about 30 years later, many of Smalltalk’s ideas still seem cutting edge.

This is Smalltalk/V as it was in the early ‘90s. Not only was it ahead of the game at that time - what is even more remarkable is that the essential features of its graphic, interactive environment and object orientation were developed as far back as the 1970s...

For a backgrounder on the history of Smalltalk see: Programming Milestones: Smalltalk.

Ruby and Smalltalk vs. Other Languages

- In this section, we look at some basic syntax

The Smalltalk/V Tutorial compares the syntax of the Smalltalk language with that of Pascal. Bear in mind that, at the time that tutorial was written (in the late ‘80s) OOP was not widely used or understood and so the tutorial had to explain ideas which are now commonplace. Even so, if you’ve never used Smalltalk, you may discover that other languages do not implement OOP in quite the same way as Smalltalk, so it’s worth taking a little time to consider even the basics . In this section, I will make comparisons between Smalltalk, C# and Ruby. Let’s first look at some simple assignment statements:

Assignment To A Scalar Variable

(See: example1.rb)

C#

b = 1;
c = 2;
a = b + c;

Smalltalk

b := 1.
c := 2.
a := b + c

Ruby

b = 1
c = 2
a = b + c
To use the sample code using Dolphin Smalltalk, open the file chapter1.st in a Workspace window (if necessary, double-click the Workspace icon in the main command window and then select File, Open). To evaluate code, select it with the mouse and press CTRL+E. To evaluate and display the result, select the code and press CTRL+D. You can also right-click and select ‘Evaluate It’ or ‘Display It’ from a menu.

The assignment operator in Smalltalk is the same as in Pascal (:=). In Ruby, it is the same as in C# (=). In C# the statement separator is a semicolon; in Smalltalk it is a period. In Ruby a linefeed is sufficient to separate statements though a semicolon may also be used and is required when separating multiple statements on a single line:

Ruby

b = 1; c = 2; a = b + c

Here’s another example

A Series of Statements/Expressions

(See: example2.rb)

C#

x = 0;
y = “answer”;
w = “hello”;
z  = w;

Smalltalk

x := 0.
y := ‘answer’.
w := ‘hello’.
z := w

Ruby

x = 0
y = ‘answer’
w = “hello”
z = w

Note that Smalltalk strings are delimited by single-quotes, C# strings are delimited by double-quotes and Ruby strings may use either single or double quotes. Ruby single-quoted strings are treated literally but double-quoted strings evaluate expressions enclosed inside #{ and }. So, if you run the following little program (ruby_strings.rb)....

puts( '#{[1,2,3].length}' )
puts( "#{[1,2,3].length}" )

The single-quoted string will be displayed literally but the double-quoted string will be evaluated, resulting in this output:

#{[1,2,3].length}
3
If you are using Ruby In Steel Developer you will be able to load up a solution containing all the Ruby and C# sample code accompanying this article. If you are using some other IDE or editor, you will need to load the Ruby files one by one.

A Function Call With One Argument

- This section looks at ‘sending messages’ and ‘calling methods’

In procedural languages such as C and Pascal, when you need to do something with a piece of data it is normal to pass that data as an argument to a function. For example, to find the size of an array in Pascal, you might pass an array variable to the size() function:

a := size(array);

Here, the size() function returns the size of the array and that value is assigned to the variable, a. In Smalltalk, on the other hand, you ‘send a message’ called size to the array object itself:

a := array size

The idea of ‘message sending’ rather than ‘function calling’ is fundamental to Smalltalk. Essentially, a message is a ‘request’ to an object. When you send to an object a message called ‘size’, that object takes a look to see if it has any way (literally any ‘method’) of responding to that message. In this case, the array has a method with the same name as the message (i.e. size) which means that it is able to respond.

The central idea of providing objects with ‘methods’ has now been adopted by most OOP languages, including C# and Ruby, though the convention of simply placing the method name after a space (as in Smalltalk) has not been adopted. Instead, dot-notation is the norm:

(See: example3.rb)

C#

a = array.Length;

Ruby

a = array.length

Note, that C# pedants might complain that in the above example, Length is a ‘property’ rather than a method. For our purposes, that really doesn’t matter. However, for the sake of completeness, here’s another example, using a slightly more verbose method:

C#

a = array.GetLength(0);

An interesting feature of Smalltalk’s ‘message-sending’ mechanism is that you are free to send any message to any object. If the object hasn’t got an appropriate method of dealing with a message, Smalltalk will inform you that the object does not understand the message and you will have the option to carry on anyhow or to debug the problem. If you try the same thing in C#, your program will refuse to compile. If you do it in Ruby, your program will grind to a halt with a ‘NoMethodError’. Ruby can, in fact, be a little more forgiving. It provides an undefined_method method. All you have to do is to implement that method in order to deal with any messages which may be sent to an object which has no appropriate method with which to respond. Try out the Ruby sample files, undefined_method1.rb (which, since the abc method does not exist, results in an unrecoverable error)...

array = [1,2,3]
array.abc

...and undefined_method2.rb which produces a nicely formatted “Cannot find method” error message and carries on running anyhow..

def method_missing(method, *args)
  if args.length == 0 then
     puts( "Cannot find a method named '#{method}'")       
  else
     print( "Cannot find a method named #{method}" )
     puts( " with #{args.length} argument(s)" )
  end
end

array = [1,2,3]
array.abc
array.xyz( 1, 2, 3 )
print( "Array length is: #{array.length}" )

The rest of Chapter One in the Smalltalk/V Tutorial is concerned with providing examples of Smalltalk syntax. I don’t propose to repeat all that information here. There are a few things worth noting, though. In particular, you will see that while Smalltalk syntax may look reasonably familiar when calling a function with one argument, things change when it calls functions with multiple arguments.

This may not be obvious when sending a message (or ‘calling a method’) such as + since this seems to behave just like an operator in any other language:

y := p + q

But how about this?

x := x1 max: x2.

This is an example of why ‘message passing’ matters in Smalltalk and how it is different from ‘function calling’. Let’s look at this closely. We’ll start at the right-hand side of the expression and work back towards the left. Here x2 is an object whose value is passed to max:. It turns out that max: is a method of the numeric object x1 (in Dolphin Smalltalk, it is actually a method of the Magnitude class from which various types of number such as Float and Integer descend). This is Dolphin’s definition of max:

max: operand
  ^self < operand
     ifTrue:  [operand]
     ifFalse: [self]

I don’t want to dwell on the finer details of Smalltalk syntax for the time being. Instead, let’s see what this method is actually doing. Essentially it performs a comparison between the value of an argument (here named ‘operand’ and the receiver object, self. By ‘receiver object’ I mean the object to which this method belongs - in the example above, that is x1. In English, the workings of this method can be expressed thus: if the value of the receiver object (self) is less than the value of some other object (operand) then return the operand otherwise return the receiver object (in Smalltalk the ^ symbol means ‘return’).

Now, Ruby numbers don’t have a max method, so how can I ‘translate’ this Smalltalk method into Ruby? There are a few possible ways. On glancing through the ruby class library I note that enumerable objects, such as arrays, have a max method. So, if I put the numbers into an array, I can do this...

(See: example4.rb)

x1 = [100,200]
puts( x1.max )

That’s a bit of a cheat though. What I really want is a max method in the Numeric class. Ruby lets me extend existing classes so I can easily add such a method:

class Numeric
  def max( aNum )
     if self <= aNum
        return aNum
     else
        return self
     end
  end
end

Now I can write something that looks much closer to the Smalltalk version...

x1.max( x2 )

Though, having done so, I am left wondering whether the effort was worthwhile. After all, it would be easy to achieve exactly the same results with a simple if..else test:

if x1 > x2
  puts( x1 )
else
  puts( x2 )
end

The above Ruby code is very similar to code you could write an procedural languages such as Basic, Pascal or C. It is most unlike Smalltalk, however. Smalltalk doesn’t do if..else tests. The reason is simple - tests like that don’t form a natural part of ‘message sending’. If if is a message, then to which object is it being sent? There isn’t one.

Here then, is one simple example of a very un-Smalltalk-like feature of Ruby. Smalltalk is quite strict in its message-passing methodology; but Ruby makes concessions to convention. Most programmers simply expect to be able to use if and else so Ruby provides them. You might say that Smalltalk is more rigorous but Ruby is more approachable. This is, I suspect, one reason why programmers often find it much easier to get to grips with Ruby than with Smalltalk.

If the lack of if tests in Smalltalk strikes you as bizarre, I should explain that these tests can be done, it’s just the way in which they are done that is different from other languages. Look again at Dolphin Smalltalk’s code for the max: method:

max: operand
  ^self < operand
     ifTrue:  [operand]
     ifFalse: [self]

Here the comparison, self < operand yields a result - which will be either true or false; that is, it will be either a True object or a False object. The True and False classes are defined in the Smalltalk class library and they have various methods including ifTrue:ifFalse:.

A Function Call with Three Arguments

Smalltalk messages and methods can, in fact, be ‘broken up’ into several pieces, with arguments (other objects) interspersing them. The Smalltalk/V Tutorial gives this example:

b := x between: x1 and: x2.

Here the message is between:and:. Once again, in Dolphin Smalltalk, this method belongs to the Magnitude class and this is its implementation:

between: min and: max
  ^self >= min and: [self <= max]

This returns true if the value of the receiver object (self) is greater than or equal to the value of the first argument (min) and less than or equal to the value of the second argument (max); otherwise it returns false. In most other languages, including Ruby, method names have just one part and arguments are sent as a list, as in this Ruby example:

(See: example5.rb)

b = x.between?( x1, x2 )

So, already a number of similarities are emerging between Smalltalk and Ruby (a fairly simple, ‘human readable’ syntax and a broadly similar approach to OOP); but we are also finding some significant differences - particularly in their approach to message passing (the Smalltalk way) and method-calling (Ruby’s more ‘conventional’ way of working).

In the next part of the series I’ll compare the ways in which Smalltalk and Ruby deal with if tests, and iteration and I’ll be taking a first look at their class libraries.

Bookmark and Share   Keywords:  ruby  smalltalk
© SapphireSteel Software 2014