Why Every Ruby Developer Should Learn Smalltalk
victorsavkin.com
victorsavkin.com
We're obviously not far from being able to have SmallTalk-quality interactions with things we build in the browser. Brent Victor's recent talk has spurred a lot of work on more-interactive development environments, some of them in the browser.
I would really like an old SmallTalker to spend some time researching and thinking about the old tools they had: which were valuable, which were more trouble then they were worth? What mistakes did they make that they wish they had done differently? It would be an excellent briefing for the next few years of work on the web.
thinking about the old tools they had: which were valuable, which were more trouble then they were worth?
A seasoned Smalltalker would have spent more time talking about the Debugger. I was teaching a Smalltalk class once, and this one student who was a quant from a New York financial firm just spontaneously exclaimed: "This debugger is from God!" It was very fast, responsive, and was "well integrated" with the environment. More on that...
EDIT: As I took the time to say elsewhere, there really isn't that much "integration." It's more that the environment is so minimalist that it lacks many barriers present in other environments. Just about every single instance of every object has the equivalent of a REPL, and finding a particular instance in the middle of runtime debugging typically takes just a second.
Even the "IDE" really isn't one. (And I should know, because I've written a good portion of a commercial Smalltalk browser.) Aside from the refactoring mechanisms, it's a pretty lightweight and basic browser on objects -- it just so happens they're the meta-level.
What mistakes did they make that they wish they had done differently?
A big mistake: exorbitant per-seat developer licenses, and positioning as the "secret weapon" of the Fortune 500. Another oft-cited one was the Parcplace/Digitalk merger and the ensuing conflicts. Most of the mistakes had to do with community and how the ecosystem did/didn't interact with the rest of the programming world. Technologically, there was a lot of goodness.
It's more what the environment didn't have. Everything, including almost all of the meta-level (and by almost, I think it's just 3 Classes that are treated specially by the VM) was just an ordinary object. Even throwing an exception was just Smalltalk executing as usual. Basically, there were no barriers.
(EDIT: An example mentioned elsewhere -- the integration between the Debugger and Unit Tests. It's not rocket science. You just catch an exception and pass an object to the Debugger.)
One headache is debugging asynchronous code, and a good debugger could track not just the current call stack, but the deeper "reason" why this code is running, including timers, events, xhr callbacks, and let us jump between those levels as opposed to just the function calls.
It wasn't unusual in VisualWorks for us to have multiple call stack open at the same time. (In fact, some people would put code to evaluate in comments, which could be executed in the midst of another debug session.) Timers, events, Semaphores -- these are all just Objects, subject to the same kind of manipulation. This isn't to say there wasn't some pain around concurrency, because there was that. But a competent programmer felt confident about dealing with it, because she/he had such responsive tools and no barriers to what they could do and see.
You could also have a Smalltalk "Process" (or green thread) starve the other ones. Lots of Smalltalks are single-threaded. VisualWorks is too, but has tricks for calling out to DLLs with a separate thread.
And the web frameworks: in many of them, you can just debug the rendering of the web page.
It's not a lot of code, but it's pretty clever.
http://www.wiresong.ca/air/2006/10/20/scripting-languages-and-ides
Basically it boils down to the semantics of the language. Languages like Java and C# can be well supported by IDEs because the syntax is declarative. A class definition in Java isn't executable, it's just data that describes the class.In Ruby, on the other hand, a class definition is executable. That makes meta-programming easy (eg. ActiveRecord's #has_many), but it makes tool support difficult. An IDE can't just parse the source code, it has to actually execute it find out what classes are defined.
But that's a huge can of worms! What if you wrap your class definition in "if Date.today.tuesday?" What if the script has side effects you don't want to trigger every time you open your IDE? On the other hand, once the metaprogram has executed, the program that actually executes at runtime isn't something the programmer wants to see. Imagine trying to work with a Rails app that had all the the metaprogramming flattened out of it? Tedious.
What's unique about Smalltalk is that it has both executable class definitions and an IDE. It works because the IDE only lets the programmer work with the runtime representation of the program, and not the on-disk representation. The whole ecosystem - language, libraries and tools - are designed around that paradigm.
There have been a few -- I think more than 3 -- Smalltalk projects where the parser was modified so that Class definition and other code could be executed but only result in a "shadow" Class being instantiated, so that the code could be browsed before being committed to the actual image.
only lets the programmer work with the runtime representation of the program, and not the on-disk representation. The whole ecosystem - language, libraries and tools - are designed around that paradigm.
It should be possible to have it both ways with Ruby. ObjectStudio Smalltalk had declarative class files. You don't want the runtime changes to be written back to the declarative files, however. This caused lots of problems in ObjectStudio. A better approach would be to have some way of flagging the runtime change, perhaps by outputting another file, then let the user merge changes back.
From the blog post.
I'd much rather see a Smalltalk that let me create small, headless images, tens or hundreds of kilobytes in size, with just the little bits of functionality I need for a particular task. If they had good libraries for file I/O, processing text on stdin/stdout and executing other commandline programs, they'd fill the "scripting language" niche very well. If they could be created and edited by a larger IDE image, they'd have the Smalltalk tools advantages as well.
Apparently someone at Digitalk did exactly this years ago, with images as small as 45kB. I think the project was called "Firewall." This technology will never see the light of day, but it shows what is possible.
forecast := service forecastForDays: 5 format: 'Xml'.
The equivalent ruby is shown as: forecast = server.forecastForDaysInFormat(5, "xml")
The example is illustrative, and a ruby code in the wild will generally use positional arguments or pass hashes around, rather than implementing weirdly named methods: forecast = server.forecast(5, "xml")
forecast = server.forecast(days: 5, format: "xml")
That is not to say weirdly named methods aren't used. Activerecord dynamic finders are one example that comes to mind `Model.find_by_first_name_and_last_name`, but they are named so to provide information to method_missing hook.> If you need to pass multiple blocks to a method in Ruby it always looks clunky and probably that's why it's not a common idiom:
service.forecastFor ->{puts "Do stuff"}, ->{puts "handling errors"}
I don't think the examples are clunky at all. It is similar in all languages which treat functions as first class datatypes.In ruby, you use "one block" or pass multiple lambdas. It isn't any different from smalltalk example, except for superficial syntactic difference.
> When you start writing a new app in Smalltalk you don't open Emacs or RubyMine you launch a VM.
I don't know much about Smalltalk, but this IDE/VM approach must be damn substantive to make up for not using Vim. The way the article puts it makes it sounds like "don't open Emacs" is a positive, which is seldom the case with people who use a lot of emacs/vim.
The thing about the IDE, is that there's hardly any of it there. It's all just objects that each effectively have their own REPL, and you're directly manipulating all of them.
You can actually start writing a debugger in most Smalltalk environments, and you'll have something that lets you browse a stack trace in under 5 minutes. (If you know the API for dealing with stack traces. A newb or someone out of practice will take longer, but most of it will be reading the API.)
An engineer I know was bored in his "Intro to Smalltalk" class and spent 10 minutes writing a tool that compiles and evaluates code. The exact same code made it into the production "IDE" and was there for over a decade.
The thing is, there's such an amazing amount of "integration" in the environment, you'd think there was tons of ingenious code written by teams of geniuses, but there isn't. It's just that a lot of unnecessary stuff has been removed and made runtime, so everything is available for you to see and change.
It's one of the best examples of how minimalism can work very well.
forecast = WeatherForecastService.for(location: 'Toronto').forecast(5.days).to_xml
The Smalltalk in Ruby makes it hard to argue that Smalltalk does this better.If you're editing your code in an editor, running it in an interpreter and storing the state in your RAM, there's no way your editor is gonna be able to stop at a point of your stack to let you modify a piece of code live, or add a method, or override an operator, or change the class of a living object and continue as if nothing ever happened.
It might not be as polished as the Smalltalk tools yet, but there's no inherent problem doing this with Ruby. There might be some things you can't do in pure Ruby, such as perhaps replacing a class pointer to actually change the class of an object, but low level hackery like that is easily enough done with a small C extension - I've poked around in MRI's C-level view of the object model before and it's pretty simple.
I think what ended up sidelining it was that it didn't really integrate well with the rest of the system. Part of this was the syntax, even though you could bridge it with C, it was awkward. IE, "something.f(a,b,c)" would become "something f: a with: b with: c". Smalltalk's syntax is really cool when you're just working within the smalltalk system itself, but at the edges it gets clunky.
The other problem is smalltalk just seems to like living in its own world. Even now that's largely the case. (Fun challenge: try making a smalltalk app that isn't easily recognizable as a smalltalk app.)
Given what Clojure is to lisp, I hope too there can be a "Clojure for smalltalk", if you will. Something that captures what's really cool about the language but throws away the historical baggage and makes it integrate well with the rest of the system.
It is true that both Lisp and Smalltalk make it very easy for people to build things that would normally take a team and an architecture. This has the down side that if it's easy to build something that acts sort of like a button, that's what ships rather than bridging to some OS button. It's a lot of work to implement for no obvious benefit, especially to the Smalltalk programmer who is using the system because they like it and don't mind the differences. This seems like exactly the sort of thing that would make for-pay Smalltalk systems viable, but nobody seems to be willing to pay for a language now. Even if it would be cheaper in the long run, it's harder to do the math and it's hard to compete with free.
> I hope too there can be a "Clojure for smalltalk", if you will.
I think this is a concept that should be explored in more depth. Some things I'd toss out as worth discussing:
- Having a standard library or syntax for defining classes (GNU Smalltalk addresses this)
- Real namespacing, because I'm sick of seeing XXYYZZClassName
- Sexy, dirt-simple FFI to C or Java. Probably, as you point out, through a standard calling convention for external stuff (maybe borrow the Block API, something f value: a value: b value: c).
I think apart from these things you could really build the Smalltalk of tomorrow in the Smalltalk of today. You need really good OS integration, especially graphically but every other way as well. You probably need to be able to serialize the system to ordinary flat text files so you can use modern VC.
I know Juan Vuletich has been rebuilding the standard image his way from the ground up with Cuis but as far as I can tell progress is stalled. Other than that I don't know of any big project besides Pharo, which is even more modest, really just about removing crap from Squeak and producing a professional shame-free open source Smalltalk.
I read somewhere that Java is to blame for that. As Smalltalk was finally gaining a footstep in the enterprise, Sun's marketing was able to convince managers to replace those projects by Java.
Eclipse design was initially based on Visual Age for Smalltalk. Actually you can still make use of a Smalltalk like browser in Eclipse.
>Fun challenge: try making a smalltalk app that isn't easily recognizable as a smalltalk app.
This can also be said of other GUI libraries. Back in the early 80-90's it was very easy to recognize Clipper, Turbo Vision and OWL applications, as the developers seldom changed the default L&F.
Thanks!
Squeak and Pharo (and VisualWorks to a lesser degree) have non-standard UIs that will be (a little) unfamiliar to Windows users. Dolphin's UI should be the most familiar.
Well, they have a "burn the diskpacks" philosophy, so the interface is very different and changes a lot of conventions.
At least I felt like I completely hosed my Smalltalk VM more than once.
This is something very different and weird you'd have to get used to. Hosing your VM is actually no big deal. There's a way to recover from that quickly without losing any source code changes. I would try Pharo. Dolphin was a nice environment, but that was so long ago.
(Since Smalltalk gives you the power to do anything, even with your meta-level, it >has< to work like this. Otherwise, people would feel timid about manipulating their meta-level.)
The online ProfStef tutorial (which is based on Pharo's ProfStef tutorial) is quite comprehensive: http://amber-lang.net/learn.html
Smalltalk is great (mostly for historic reasons these days) and definitely worth learning. But that bit is cognitive dissonance at its best.
Smalltalk is great (mostly for historic reasons these days) and definitely worth learning. But that bit is cognitive dissonance at its best.
It allows you to extend even the Boolean Logic of your system. With just an ordinary act of defining functions, you can implement something like:
(condition)
ifTrue: [ "true" ]
ifFalse: [ "wasn't true" ]
ifMaybe: [ "whoa, toy fuzzy logic!" ]
The same goes for looping and other control structures. This makes any DSL you write a first-class language construct equal to anything else in the language.The point was that this followed immediately an explanation of how much more readable (!) the OO syntax was because there was no separate operator (".") for method invocation. If the former is seriously a criteria for "being better than ruby", the Smalltalk condition trick is an abomination, and immensely inferior.
edit to avoid thread continuation: yet again the response boils down to "it's easy if you know it". So to be clear: 1.) I do know it; and 2.) Duh. But the previous point in the blog post seems to be predicated on the idea that learning the "." operator for method dispatch in ruby is somehow "hard" and that it's an advantage to Smalltalk that it has such a straightforward syntax. Those two opinions cannot logically be held in the same brain.
Browse menu, "implementors," select ifTrue:ifFalse:ifMaybe: and find yourself in a short method in under 3 seconds. (Most of that human menu-selection time.) Maybe another 5 seconds for someone familiar with the environment and its conventions to read and understand what they are seeing.
You have a different POV, because it's the POV of someone who doesn't yet know how to fend for yourself in a different environment. Once you learn a few tricks like this, it's very empowering. (And it's not an obscure trick. A seasoned Smalltalker lives by "senders" and "implementers")
Those two opinions cannot logically be held in the same brain.
I don't hold those two opinions. I don't think . is hard, and I don't think Smalltalk syntax is so much easier. What it is, combined with code blocks as literals, is a meta-syntax. I think it's more powerful in some ways.
1. He didn't say Smalltalk's syntax was learnable — he said it was "readable". He'd probably agree that it's more learnable too, but his actual claim was more like "It's easier to mentally tokenize code that uses spaces to separate elements than code that uses dots." This is similar to how many Rubyists feel that using whitespace instead of parens can make code more readable.
2. He acknowledged that it will be awkward if you're already well-versed in languages that do things differently. But this is tautological. Attacking a programming language for being different from other programming languages is a lot silly.
3. It is true that Smalltalk's pervasive object-orientation will be unfamiliar for people whose experience is limited to Algolish languages, but continuing off 2, that isn't really a bad thing. Smalltalk's model is more consistent than Ruby's. There are basically two things in the whole language: message sends and variable definitions. Your objection here isn't that there's a new abstraction, but that there isn't anything new — it's objects all the way down. It's new to you because you know Ruby, but it's not really a new thing in the course of learning Smalltalk.
In my experience coming to Pharo Smalltalk after a 15 year journey through C, C++, VB, PHP, ObjC, and Ruby (my second favorite lang), I can say that none of the reasons I find Smalltalk awesome are historical. This idea that one should "learn Smalltalk to bring those lessons with you to the language you 'really' plan on using" is a silly idea, often repeated.
Most importantly, Smalltalk is an environment, not a language...
The Smalltalk community is full of talented developers doing cutting edge research and serious commercial applications. We use Smalltalk, for example, because its live, open, dynamic environment makes it a joy to use and blows productivity through the roof. Having complete control over the IDE is a dream - as a small example, when I got tired of constantly navigating to my default code directory, I added a default setting to Pharo Smalltalk, in about 5 minutes, so I and every other user could benefit. This kind of enhancement is routine because it's so damn easy! Even in Ruby, it's a hassle to dig through a lib's source files and find the code to edit. In Smalltalk, one can drill down through the UI (from menus, windows, etc.) into the actual code, or set a breakpoint launching a debugger on the running system. What other OS (Squeak/Pharo can run on bare metal) lets you do that, all in the same language?!
There are Ruby environments that provide fairly deep introspection and live changes to a system, like Pry. It's just not the main way of doing things. For a reason: It's not the way most developers are used to working.
They are both tools, and thus we're primarily concerned with their utility, not inherent value. One wants to know: how will these tools help me to achieve my goals. The answer is that the turtles-all-the-way-down experience of Smalltalk helps devs achieve those goals more quickly, easily, and joyfully.
p.s. Thanks for pointing out Pry! I didn't know about it and it looks really cool :) As it relates to this conversation though, a more-advanced repl is stone-age technology.
VB6?
I found the following particularly useful: 'Smalltalk by Example', by Alex Sharp; 'Smalltalk With Style', by Edward Klimas, Suzanne Skublics and David A Thomas.
This is a great book for beginners in any OO language: 'Smalltalk, Objects, and Design', by Chamond Liu
Also surprisingly useful (terse, but well structured): 'On To Smalltalk', by Patrick Henry Winston
Finally, there's the excellent ProfStef interactive tutorial that's included with Pharo (http://www.pharocasts.com/2010/01/learn-smalltalk-with-profs...).
I know this, because you sure as hell aren't spending that time doing something useful and worthwhile.
EDIT: [REDACTED]
EDIT2: In hindsight, probably not nice to make fun of somebody's product. I know I wouldn't appreciate it.
I mostly hack C and C++, graphics, games, containers, tests--take your pick, coffee and pistols for two at dawn. :)
The thing is, you really ought to expose yourself and at least play around in other languages. No language (except, perhaps, APL, assembly, Forth, Lisp, or Prolog [not that there exists a standard variant of the last two]) is perfect.
Every language does some things really well, other things really poorly, and muddles through the rest. Being familiar with other helps you know when your language of choice might need to call out to something better suited to your problem domain.