Why I love Smalltalk

Smalltalk logo

This post was extracted from a small talk I gave at Simplificator, where I work, titled “Why I love Smalltalk and Lisp”. There should be another post, “Why I love Lisp” following this one.

After I learned my basic coding skill in more or less traditional languages, like C, C++, Python, there were four languages that really taught me something new. Those languages changed my way of thinking and even if I never use them, they were worth learning. They are:

  • Smalltalk
  • Lisp
  • Erlang
  • Haskell

You can probably add Prolog to that list, but I never learned Prolog. This post is about Smalltalk.

My goal is not to teach Smalltalk but to show things that you can do with Smalltalk that you can’t do with any other language (disclaimer: surely other languages can do it, and we’ll call them Smalltalk dialects). Nevertheless I need to show you some basics of the language to be able to show you the good stuff, so here we go, a first program:

1 + 1

That of course, evaluates to 2. If we want to store it in a variable:

m := 1 + 1

Statements are finished by a period, like this:

m := 1.
m := m + 1

In Squeak, a Smalltalk implementation, there’s an object called Transcript and you can send messages to it to be displayed on the screen. It’s more or less like a log window. It works like this:

Transcript show: 'Hello world'

and it looks like this:

Squeak transcript showing the result of Transcript show: 'Hello World'

The syntax is quite unique to Smalltalk. The message, otherwise known as “method call” in other languages, is called show: (including the colon) and it takes an argument. We can run it 10 times in a row with the following snippet:

10 timesRepeat: [
  Transcript show: 'Hello world'
]

There you can start to see how Smalltalk is special. I’m sending the message timesRepeat: to the object 10, an Integer. Doing something N times repeatedly is handled by the Integer class, which if you think about it, makes sense.

The second interesting part, is the block. The part inside squared brackets. You might thing that’s the equivalent of other language’s block syntax, like in this Java example:

for(int i=1; i<11; i++) {
  System.out.println("Hello world");
}

but Smalltalk version’s is a bit more powerful. It’s a real closure. Look at this:

t := [
  Transcript show: 'Hello world'
]

Now I have a variable named t, of type BlockClosure, and I can do anything I want with that variable. If I send it the class message it’ll return its class:

t class

and if I sed it the value message, it’ll execute and leave a “Hello World” in the transcript:

t value

Let’s see some more code. A message without any arguments:

10 printString

a message with one argument:

10 printStringBase: 2

and a message with two arguments:

10 printStringBase: 2 nDigits: 10

Isn’t it cute? That method is called printStringBase:nDigits:. I never seen that syntax anywhere else; well, except in Objective-C, which copied it from Smalltalk.

Enough toying around, let’s start building serious stuff. Let’s create a class:

Object subclass: #MyClass
       instanceVariableNames: ''
       classVariableNames: ''
       poolDictionaries: ''
       category: 'Pupeno'

Notice that a class is created by sending a message to another class telling it to subclass itself with the name and a few other arguments. It’s a message, a method call like any other. Object is a class, classes are objects. The object model of Smalltalk is a beauty but that’s a subject for another post.

Now that we have a class, let’s create a method called greet: in that class.

greet: name
  "Greets the user named name"

  | message |

  message := 'Hello ', name.
  Transcript show: message.

In that method definition first we have a comment for the method, then the list of local variables within pipes (“|”), and then the implementation, which sets the variable message to contain “Hello ” and the comma concatenates name to it. Then we just send it to the transcript.

It looks like this:

MyClass greet method

Ok, let’s use it:

m := MyClass new.
m greet: 'Pupeno'

To create an object of class MyClass, we send the new message to that class. There’s no new keyword like in Java. new is just a method. You can read its code, override it, etc. Don’t mess with it unless you really know what you are doing.

Actually, if you think about it, we haven’t seen a single keyword. Look all the code we wrote without having to memorize any keywords! What’s even more important is that by now you essentially know Smalltalk. That’s all there is, but like LEGO bricks, this simple and small building blocks allow you to build whatever you want.

Yes, that’s it, that’s all there is to it. We already saw that Smalltalk doesn’t need loops, it has integers and that class implements the timesRepeat: message which allows you to do something N times. There are many other looping methods here and there.

What about the if keyword you ask? Surely Smalltalk has an if? Well, no, it doesn’t. What you can recognize as an if is actually implemented in Smalltalk using the same mechanism of classes and message passing you saw already. Just for fun let’s re-implement it.

We starte by creating the class PBoolean and then two classes inheriting from it, PTrue and PFalse.

Object subclass: #PBoolean
       instanceVariableNames: ''
       classVariableNames: ''
       poolDictionaries: ''
       category: 'Pupeno'

PBoolean subclass: #PTrue
       instanceVariableNames: ''
       classVariableNames: ''
       poolDictionaries: ''
       category: 'Pupeno'

PBoolean subclass: #PFalse
       instanceVariableNames: ''
       classVariableNames: ''
       poolDictionaries: ''
       category: 'Pupeno'

For the class we created before, MyClass, we define a equals: method that will return either true or false, or rather, PTrue or PFalse.

equals: other
  ^ PTrue new

The little hat, ^, means return. For now, just a hardcoded true. Now we can do this in the workspace:

m1 := MyClass new.
m2 := MyClass new.
m1 equals: m2

and get true, that is PTrue, as a result. We are getting close but no if yet. How should if look like? It’ll look like something like this:

m1 := MyClass new.
m2 := MyClass new.
(m1 equals: m2) ifTrue: [
  Transcript show: 'They are equal'; cr
] else: [
  Transcript show: 'They are false'; cr
]

and you can start to imagine how to implement it. In PTrue we add the method:

ifTrue: do else: notdo
  ^ do value

That method basically takes two parameters, evaluates the first one and ignores the second one. For PFalse we create the oposite:

ifTrue: notdo else: do
  ^ do value

and that’s it. A working if! If you ask me, I think this is truly amazing. And if you check Squeak itself, you’ll find the if is actually implemented this way:

True's ifTrue:ifFalse:

If your programming language allows you to create something as basic as the if conditional, then it allows you to create anything you want.

Advertisements

40 Replies to “Why I love Smalltalk”

  1. Thank you so much, this is truly eye opener.

    PS: #Pfalse is missing, you defined #Ptrue twice, I believe that this is not what you intended to do.

      1. This beauty and simplicity in language constructs can also be seen in the lambda calculus itself, concatenative languages (like Factor) and Lisp dialects (like Racket) — or even more in Kernel, where you can derive all constructs in the language from 3 primitives (no special forms).

        But Smalltalk has a lot of neat things going for it indeed. Have you looked at Self and Newspeak yet? The latter is still my favourite Smalltalk dialect.

  2. Just a typo here : “What’s even more important is that by now you essentially now Smalltalk”
    You probably mean “know Smalltalk”

    BTW nice post. I didn’t know that this language is still used. I remember have seen it last time in 1993.

  3. SmallTalk was also a foundational language for me. As were Prolog and FORTH. Prolog was interesting but I have found few uses for it. FORTH OTOH is an ever-present member of my toolbox, primarily as an embedded debug tool in pretty much everything I do, both major OS apps and embedded systems. So much so I have written a tool that generates FORTH wrappers from a C .h file.

    I stopped using SmallTalk after discovering a side-effect of the Closure definition that (as I was told, snottily, by an engineer at DigiTalk) was as described in the language definition:

    You can declare local variables in a closure. If you then run more than one instance of the closure – e.g. using fork: – you do not actually get multiple instances of the closure. There is in fact only one instance and all parallel invocations use the same local variable. Yes, there are multiple somethings doing work, but the state is shared between them. So not really closures at all. Either that or fork: needs re-defining so it does make a copy.

    Because I have some SmallTalk in my cv, I occasionally get head-hunter queries. Seems SmallTalk is still used heavily in banking applications.

    BTW, Objective C borrows heavily from SmallTalk in the area of call syntax.

    1. I am not sure about Squeak, but in VisualWorks (which is also free for private purposes) blocks are reentrant since its first release. As far as I remember (but I may be wrong), the reentrance problem was an issue in ObjectWorks (the predecessor of VisualWorks. Closures are really “closed” and don’t share local variables’ state.
      You’re right: Smalltalk is widely used in industry. And it’s not only banking stuff. I was member of the leading dev group at AMD where they implemented a major part of the chip production software which controls the machinery to produce computer chips (CPUs). Other projects of mine are web applications built with seaside, a web app dev framework which is available for most Smalltalk dialects.

      1. This sounds like the same “limitation” that Squeak (and its derivatives like Pharo) had until recently – block temp variables were in fact stored in the method context where the block was created. This prevents proper recursive use of blocks as true closures. BUT this has been fixed and these days all (AFAIK) Squeak derivatives and certainly most commercial Smalltalks like VisualWorks do have proper real closures.

    2. Not sure what you mean about more than one instance of the closure. It’s an object, not a class of objects. Would you expect a single object to have a different state each time it’s used, whether it’s used in multiple threads or a single thread?

    1. Coding on Ruby puts the food in my plate. The problem with Ruby is that the syntax for creating blocks is not as nice as Smalltalk and blocks passed  to methods require a special syntax and there can be only one per method. There are workarounds but they are ugly.

      1. Care to share workarounds with me. I am having similar thoughts about blocks (only one, and syntax which is inferior to Smalltalk). The thing is I thought Ruby blocks are great (I still think they are), but they seem limited compared to the “origin. I was watching Matz talk on Ruby 2.0 features, there is some talk that after keyword arguments support gets enables it will be easy to support multiple blocks in method calls. (Hash for keyword arguments can get you only so far). Btw. the other improvement in Ruby 2.0 will be traits, which is mixins done right (ofcourse from Smalltalk).

      2. In Ruby it would be something like

        b.my_if(lambda {
          puts "It's true"
        }, lambda {
          puts "it's false"
        })
        

        and the implementation of would be:

        class True
          def my_if(then, else)
            then.call
          end
        end
        
        class False
          def my_if(then, else)
            else.call
          end
        end
        

        Possible but ugly.

      3. I can’t reply to your answer down… yes I agree ruby version it is ugly. Lambda syntax with -> is helping a bit, but if feels unnatural and perlish :) Smalltalk is pulling it off ok, with multiple blocks support and better block literals, and Clojure is of course right at home with macro chainsaw. I am hoping for things to get better in Ruby 2.0 but I am not sure how much can be done, since Smalltalk is capitalizing on using dot for “end statement” and not method call. And exactly that is getting in a way for Ruby syntax.

  4. My first professional language was lisp – then Smalltalk. It took me about six months or rewiring my brain to really start thinking in Smalltalk. It’s still wired that way, and often causes me problems in languages that are built with special cases instead of a clean, simple, intuitive, class and metaclass semantics.

    If they would have just got the team programming part right … We wouldnt have needed Java.

    Still … Such brilliance and elegance. I miss it.

    Ken

  5. The syntax of Smalltalk can be described on one side of a sheet of A4 (or Letter) paper. That is fantastic. Try doing that with other languages and have it remain readable.

    1. You could do this with Lisp and with Forth; if I understand Prolog correctly, with Prolog too. You could even do that with a well-designed assembly language (MIPS does this, if I recall correctly).

      I don’t think it’s a coincidence that these languages also happen to be the most powerful ones! (As much as I am fascinated by Haskell, I would consider this to be one of its shortcomings…)

      I once tried to create a BNF definition for Forth, and was blown away by the fact that it was basically “just divvy up by spaces, the rest are tokens”!

  6.  thank you for that simple and convincing text. Exactly this experience of Smalltalk thrilled me many, many year ago. Therefore it became my mother tongue amount the programming languages. And I still don’t know, why it is not that convincing for many people.

  7. “It’s”
    is a contraction for It Is. It isn’t possessive. I couldn’t get past the bogus
    grammar.

  8. Ruby got all that save for Squeak.

    But one day it will have something similar to Squeak. Probably a year after Rubinius 2.0.0 comes out.

  9. It is a very nice article. But you hardcoded the equals method to return PTrue. I was wondering how can one implement it properly(seen that we do not have an if-else)?

    1. The equals in MyClass would call equals in another classes, like:

      (self name) equals: (other name)
      

      where name would be, let’s say, a string. So then you need to implement an equals: for string, which would compare each character. Something you can also implement in Smalltalk. At some point, sooner or later, you are comparing numbers, at that point you have a comparison that it’s not really written in Smalltalk, or it is, but gets compiled, if you go deep enough, to the processor assembly comparison calls. I haven’t actually done the research on this and I’m just supposing, but you could download Squeak and see how deep the rabbit hole goes.

  10. Welcome to the Smalltalk mind-meld Pablo. :)

    One of my favorite Smalltalk examples of the cool/interesting things you can do when you have first class block closures that are easy to use in literal form, is the Dolphin Smalltalk implementation of do:separatedBy:

    do: operation separatedBy: separator
    | sep |
    sep := [sep := separator].
    self do:
    [:each |
    sep value.
    operation value: each]

    I believe the implementor was a former Lisp’er. One person once commented to me that Smalltalk blocks took the Lisp lambda, and made it easy to use.

  11. Thanks for a very nice article!

    I’ve had the good fortune to be exposed to Smalltalk in my teens, but never got any real practice with later.

    When you send a message that the receiver does not understand (ie. “when you try to call a method on an object that doesn’t implement that method”) it instead re-sends the message doesNotUnderstand: with a parameter detailing what exactly wasn’t understood. This sounds like an exception, but it’s really just an ordinary message (“method”) inherited from Object, and you can replace it. (The default implementation is to start a debugger or something.)

    So I remember an article complaining that some complicated classes might have messages with lots of parameters where it’s easy for a programmer to forget the exact order of keywords, e.g. searchBackwards:step:with:using:interleave:except:foo:bar:. The solution was to redefine doesNotUnderstand (in that class or in Object) to send a new message tryToUnderstand: which would try to permute the keywords (and arguments) so they fit the class :)

    Reflection is so trivial here you don’t even notice it as a special concept at all. Many modern industry workhorse languages could pull off similar tricks, but it would be some deep hackery that 90% of professionals couldn’t reproduce without a few hours of trial and error and poring over the docs. In Smalltalk, it was just 6-10 lines of clear, obvious code.

    1. The equals: is hardcoded for simplicity, but we can try to dig a little bit deeper there. What would be want equals: to do? Maybe compare the members. That class has no members but we can imagine it contains a name member variable. Then equals: would be something like:

      equals: other
        ((name class) equals: (other class)) ifTrue:
          ^ (self name) equals: (other name)
        else:
          ^ PFalse new
      

      The first line is just to make sure they are the same class, otherwise, they are different. Then I compare the name and if that is equal, I consider the object equal. Now we pushed the problem into defining equals: for classes and strings. For classes maybe we compare a global id of the class and for strings each of the characters. We keep pushing the problem until we reach the comparison of a single integer, which is something the microprocessor or the virtual machine implements. It would be interesting to see how it looks like on Squeak, but I haven’t searched for it.

      The important part though, is that even if at some point we hit some magic, we are talking about equals:, about implementing the comparison between two things, not about implementing if. If is done, implemented and reusable. And this is how it’s done in Smalltalk itself, except that the method is ifTrue:ifFalse: and the comparison is == if I’m not mistaken (I don’t use Smalltalk daily).

  12. Sebastian, the equals: method was not what Pablo was trying to show – you can implement it either way you like, he just hardcoded it to true so that he could carry on with his implementation of if-else. The trick in Smalltalk is that everything is an object (even booleans) and we have two different instances of two different subclasses (these classes are called True and False) that can respond to the same messages but do different things (=polymorphism). Thus, we solved the if-else control structure using booleans as objects, polymorphism and having true closures (blocks).

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s