Ruby vs. Smalltalk

Hmm. No headers (<h1>, <h2>, etc.) allowed in here. Use COBOL-style level numbers to express an hierarchy of headers?

01 Semantics

Ruby has mixins; Smalltalk does not. Mixins help much in design.

Ruby permits adding methods to individual objects; in Smalltalk, all methods reside in classes.

In Ruby, it is practical and somewhat useful to add methods dynamically; in Smalltalk, the practice is generally to treat the methods and classes as static.

Ruby offers powerful macros in class definitions; Smalltalk offers no macros at all.

01 Syntax

02 Literals and Constructors and Destructors

03 Array Constructor

Ruby offers a very convenient syntax for constructing an array from expressions for its elements. I think all modern Smalltalk implementations also support this, although it is relatively new in the history of the language. If a Smalltalker reading this knows of a Smalltalk implementation in commercial or wide use that doesn't support the syntax "{foo. bar. bletch}" (note that there are spaces after the periods here; these are sentence-end periods, not namespace-qualification periods) for constructing an array, equivalent to "(OrderedCollection new add: foo; add: bar; add: bletch) asArray", please add a comment to that effect.

In array construction in Ruby, you can say something like "[foo, *bar]" to mean the array whose first element is given by the expression foo and the rest of whose elements are given by the array given by the expression bar. In straightforward Ruby not using the "*", this is "[foo] + bar" (+ is overloaded for concatenation in Ruby's library). In Smalltalk, it would be "(Array with: foo), bar", where the comma operator is used for concatenation in Smalltalk's library. Interestingly, the "*" syntax can also be used for destruction. You can say, for example

[foo, *bar] = bletch

where "=" is the assignment notation (discussed below) and bletch is an array of at least one element. Then foo will be set to the first element, and bar will be set to the rest. This example emulates Joy's "snoc" operator (cons spelled backward). See also, multiple assignment, below. Smalltalk has nothing like the conveniences offered by Ruby's prefix "*" notations.

Ruby also lets you use the prefix "*" in argument lists to construct argument lists and in parameter lists to deconstruct argument lists. Perhaps as a Smalltalk advocate in regard to keyword arguments vs. positional arguments, however, I might not think this usage brings much to the party as an advantage of Ruby over Smalltalk. It does, in the Ruby context, bring something to the party, just as the equivalent construct in parameter lists in Lisp ("&REST") makes Lisp much more flexible. Who is to say whether positional parameters should be basic and keyword: value pairs should be packed up in a dictionary and passed as one of the positional arguments, or conversely, whether keyword parameters and arguments should be basic and if you want to pass a list you pack up the list as an array and pass it as one of the keyword arguments? Smalltalk takes one attitude and Ruby the other. Personally, I'm in favor of keywords.

03 Dictionary Constructor

Ruby offers a very convenient syntax for constructing a dictionary (a dictionary is called a Dictionary in Smalltalk and (unfortunately) a Hash in Ruby; I'll use the term dictionary as the generic term). The constructor is short to write, and keys and values can come from any expressions. Example, "{:foo => :bar, :bletch => 3}", which in Smalltalk would be "Dictionary new at: #foo put: #bar; at: #bletch put: 3; yourself". Note how much shorter "{:foo => :bar, :bletch => 3}" is than "Dictionary new at: #foo put: #bar; at: #bletch put: 3; yourself". This leads to significant economy of writing, if you are passing around many constructed dictionaries, which in Web development, for example, tends to be done quite a bit. I'll preempt whoever is going to point out that much of the dictionary construction in Ruby is being done to overcome the lack of the keyword message call (discussed below), by saying that dictionaries have other uses than immediate use in a message call. Sometimes you would like to pack up the message and treat it as a whole. Oz/Mozart recognizes this as Ruby does (although details differ).

03 String Constructor

Ruby offers a convenient syntax for constructing a string. Parts of the construct can be literal, and parts can invoke expressions for string values to be interpolated into the string under construction. Example: Ruby:

    "Hello, #{name}!"

Smalltalk:

    'Hello, ', name, '!'

At first glance, the reader might not see much economy in the Ruby syntax over the example I gave in Smalltalk; however, if you are writing many constructions of strings from alternating literal and variable parts, the Ruby syntax becomes pretty convenient. I think it is less error-prone than a long sequence of concatenations given explicitly, with many literals interspersed. Moreover, experienced Smalltalkers know that abuse of the comma operator can lead to significant inefficiencies, where abuse consists of building up a long string (e. g., a report) via many concatenations. The Smalltalk library implements the comma operator inefficiently by copying over the string every time. Consequently, we get in the habit of writing an example like this this way:

WriteStream new
    nextPutAll: 'Hello, ';
    nextPutAll: name;
    nextPutAll: '!';
    contents

(the semicolon is used in Smalltalk for the cascade syntax; it means send multiple messages to the same receiver). If Ruby's concatenation (uttered as "+") is ever found to be inefficient like Smalltalk's, I'm sure that what the language culture would lead to is making it work as efficiently as though implemented by Smalltalk's WriteStream, not to inventing a new class equivalent to that class in Smalltalk. The general attitude difference seems to prevail, that in Ruby it's more, write what you mean, and leave efficiency to the language (or library) implementation.

03 Regular Expression Literal

Ruby offers a very convenient syntax for expressing patterns over strings, i. e., regular expressions. It looks just like in the Unix "ed" line editor, with the pattern between slashes. Smalltalk has nothing of the kind, in its syntax.

03 Alternative Syntaxes for Literals

Ruby has several other literal syntaxes for convenience, that Smalltalk does not.

02 Assignment operator

Smalltalk's assignment operator is ":=", which looks unambiguously like assignment and not equality, which I consider very appropriate for an imperative language. The Ruby designer (who nevertheless I thank profusely (if he's reading this) for the good features of the language, which I think occur together in no other, so the combination of them makes for a helpful innovation, and for providing the first interpreter) erroneously chose to follow Fortran and its kin in using naked "=" for assignment.

On the other hand, Ruby offers multiple assignment. You can swap two variables by saying "a, b = b, a". In this vein, see also the prefix "*" notations I mention above. Smalltalk does not have multiple assignment.

Ruby offers convenient translations of assignment syntax into message calls. Smalltalk: "foo at: x put: y". Ruby: "foo[x] = y". You write what you mean, not how it is implemented. Smalltalk: "aircraft altitude: 3600"; Ruby: "aircraft.altitude = 3600". In each of these examples, Ruby translates the assignment into a call that can be fielded via the normal method dispatch mechanism.

02 Message-call syntax

03 Keyword Messages

Smalltalk has keyword syntax for putting arguments (and parameters) in the middle of a selector; Ruby lacks this feature. I think this is one of the best contributions of Smalltalk to the history of programming languages. Keywords make a program much easier to read, I think, because they tell the reader the meaning and intent and significance of the parameters and arguments, in the sense of their participation in the design behind the program. In Smalltalk, the keywords contribute to the selector, so they participate in the message dispatch. This supports the writing of distinct methods for handling different cases of what data are available to construct or calculate some desired result.

Ruby somewhat makes up for the lack of keyword message syntax with the dictionary constructor syntax. In fact, in the context of a message call, Ruby makes the syntax for specifying keyword-argument pairs even easier, almost comparable to Smalltalk's keywords. Let's look at an example. Consider, in Smalltalk:


    viewer cueMenuRoleNamed: aName forPage: aPage andLayout: aLayout

In Ruby, we could write:


    viewer.cue_menu_role :role_name => a_name, :page => a_page, :layout => a_layout

In this Ruby example, the programmer was able to omit the braces that would otherwise have to enclose (and form part of) the dictionary constructor because the dictionary is the last argument and the syntax allows that omission in that circumstance, as a notational convenience. Note how closely the Ruby example parallels the Smalltalk. But there is an important difference in the semantics here. In the Ruby example, the selector is only "cue_menu_role". If the "cue_menu_role" functionality is to operate with different combinations of given data in addition to the selection exemplified (role name, page, and layout), the method or methods that implement "cue_menu_role" will have to examine the argument list and distinguish the cases. In the Smalltalk example on the other hand, the selector is "cueMenuRoleNamed:forPage:andLayout:". The selector embodies the selection of available input data, so the method that implements it only has to deal with that case.

In favor of the Ruby approach, on the other hand, is the fact that in Smalltalk, the order of the keywords matters, but in Ruby it does not. Usually in the English that the programmer is trying to get the reader to think of as the interpretation of the operation, the order doesn't matter, so for example, it's a nuisance to have to implement both ifTrue:ifFalse: and ifFalse:ifTrue:. However, I suppose there are counterexamples in the underlying English. Sometimes, order matters in English.

So, in looking at keywordism as a whole as addressed in the two languages, I see some strength in Smalltalk in that the keywords are part of the selector, and that the use of keywords with message arguments right at the point where you want to trigger the dispatch is syntactically simple and easy. I see as weaknesses in Smalltalk that the order matters, and that it is not so easy to package up the collection of keyword-argument pairs as a message that can be treated as a whole.

03 Syntax Between Receiver and Selector, and Treatment of Juxtaposition in the Syntax

In Ruby, you say "foo.bar"; in Smalltalk, it's "foo bar". The notation for a unary message call is cleaner in Smalltalk than it is in Ruby, because simple juxtaposition, I'd say, requires less mental effort to read, than the notation using the dot. Of course I don't have any cog-sci data about mental effort; I'm just saying that it feels cleaner to me.

The syntax makes better use of juxtaposition in Smalltalk. In Ruby, juxtaposition has been used to separate selector from arguments. For example, "foo.bar bletch, baz" would mean what "foo.bar(bletch, baz)" means in Ruby. This is arguably not a terribly wasteful use of juxtaposition in Ruby's syntax. It makes for a clean, "command" look, especially in cases where the receiver is implicitly "self" (discussed below). "load 'foo.rb'", for example, looks cleaner than "load('foo.rb')". But the direction of development of Ruby seems to be away from a consistent use of juxtaposition in this way. If you write, for example, "foo (bar)", the complier might warn you that you ought to remove the space to anticipate the language's future. So, in summary about the dot and juxtaposition, I say that although the use of juxtaposition in a command like "load 'foo.rb'" is quite nice, Smalltalk rather than Ruby has made the better choice about where to harness simple juxtaposition in the syntax, in that Smalltalk grabs this for its basic message-call syntaxes, to separate receiver from selector.

03 Implicit Self

In Ruby (as in the language Self, the pioneer in this regard, and from which the language Self gets its name), you can usually abbreviate message calls on "self" by omitting the mention of "self". For example, for "self.foo" you can write simply "foo". But in Smalltalk, you cannot abbreviate "self foo" by writing simply "foo". This makes a major economy in writing and reading Ruby code.

03 Strange Syntax for Blocks

In Smalltalk, there is easy syntax for constructing a closure and passing it as an ordinary argument and picking up that argument with an ordinary parameter and then for invoking the closure and/or passing it on. Ruby, semantically, has the same capabilities for constructing, passing, and using closures. However, Ruby uses oddball syntax for these things, for no benefit that I can discern. There is a specially distinguished argument position available in a call, specifically for passing a closure. If you are going to construct a closure in that position, the syntax is simple. But the closure in its simplest syntax is not an expression. It only goes in that special position in the syntax, or has to have a word put in front of it, "lambda", to be turned into an expression that could be put in an ordinary argument position or any other expression context (e. g., right side of assignment). If a method is expecting to receive a block as an argument, the designer has to choose between having it take the block as an ordinary argument or as the special block argument (either can be chosen, in the monomorphic case). And if the method needs to take two blocks, at least one of them has to be passed as an ordinary argument, so the decision has to be made whether to pass one as the special argument and if so, which one. Conversion in both directions is available in all contexts where it makes sense, between the special argument or parameter and an ordinary expression. I think this exceptionalism in Ruby's syntax imposes significant extra conceptual load to understanding the syntax. Smalltalk's treatment of blocks is plenty economical, whether they are being passed as arguments right at the point of construction or not.

I guess I can see why this exceptionalism arose; it avoids having the closing parenthesis of the argument list coming right after the end of a block, which I can see would look ugly. But, so much twisting and turning and squirming for a tiny increment of beauty.

03 The Cascade

Smalltalk has the cascade syntax, which Ruby lacks. Perhaps this lack is not as costly to Ruby programmers as it would be to Smalltalk programmers if the cascade syntax were dropped from Smalltalk, because in cases where the receiver is self, a cascade would buy nothing in Ruby because you can leave out the receiver in those cases anyway.

03 Special Characters Permitted in Selectors (speaking here of selectors other than "+", "*", and the like)

Ruby permits "!" in selectors, which is useful to warn code readers that a functional style is not being used. Smalltalk allows only alphanumerics in its unary selectors and in the keywords of its keyword selectors (not counting the colon, which has a specific role in the syntax even though the jargon of discussion of Smalltalk usually speaks of the "keyword" as including the colon). Ruby also permits "?", but below, I argue that this doesn't help because I prefer Smalltalk's convention for choosing selectors indicating Boolean results.

02 Control Constructs

Smalltalk's syntax includes no control constructs except the "^" (early return). Any kind of "if", "while", etc., are made by message calls using blocks. At least, syntactically they appear that way. As the Self designers point out, in reality you can't override ifTrue:ifFalse: unless the Smalltalk compiler resorts to the sophistication characteristic of the Self compiler. If you could, many programs would run too slowly because they are laced with much conditional code. But anyway, syntax-wise, the control constructs don't have their own syntax in Smalltalk, other than the "^". Ruby on the other hand, includes syntax for a number of common control constructs even though blocks (closures) are almost as convenient to express in Ruby as they are in Smalltalk (but not quite; sometimes you have to throw in an extra word to make the block an expression). When I was a religious Smalltalker, I might have laughed at languages with control constructs and praised Smalltalk for its syntactic simplicity in using pure object orientation to express the control constructs. But I can also see a Rubyist's point that these control structures are in common use in all imperative programming, so why not have the syntax make it straightforward for a programmer to just write what she means? It doesn't cost much.

02 Returned Value

Mentioning "^" above reminds me, that in Smalltalk, if you don't explicitly write "^" with an expression (which means, return the value of the expression as the result of the call), a Smalltalk method returns "self" (I mean, what "self" means in the languages, not the string "self"). In Ruby (as in Lisp), the last expression in the method gives the value returned (absent an early return). This supports a functional mindset; I have written any number of methods that consist of just one expression. Also, it says that if you are not doing an early return, which is in fact a control construct that violates Dijkstra's structured programming, and that should stand out as a warning to readers, you don't have to write "return". I'm not saying I'm against early return; it can be very convenient and can contribute greatly to brevity. I'm saying, early return should stand out. In Ruby you can make it stand out by uttering "return" only in the case of early return. In Smalltalk, you have to write "^" something or you'll get "^ self".

For any Smalltalker who argues in favor of the programming style that says a method (or rather the whole suite of methods implementing the same selector) should be either designed to return a value or to have a side effect but generally not both, I agree; sometimes I prefer that style. And Smalltalk's default supports that style. But in Ruby, you can, with ease and clarity and economy, stanch any accidental return of a value some other programmer might seize on, by just ending your method with "true", having it return the Boolean true to indicate success in achieving the desired side effect. I think Smalltalk's support of returning self by default is more costly than beneficial, because I think the two considerations in the paragraph just above outweigh the advantage of support for ask-or-tell-but-not-both-at-once style via the default.

On edit: A comment below by user "renox" refers to a lesson from E, that there can be significant hazard of accidentally returning an object you don't want to. I accord that argument much weight, and I question the opinion I gave in my initial version of this section, strongly in favor of having the last expression be the returned value by default. Anyway, that is one of the differences between Ruby and Smalltalk, and it affects the flavor of the way they read, I find.

01 Declarations

Smalltalk requires declaration of all local variables and instance variables. In Ruby, you introduce both simply by usage; Ruby does not require declarations for them. This makes a major economy in writing in Ruby.

01 Scope

Ruby absolutely makes the wrong choice by denying the programmers any way, just in the syntax in a block of code as you could observe without looking outside the block, to specify a local variable scoped just to that block. You can write, for example, "foo" as the name of your variable, and you can limit its use to the block, and you can hope it will be scoped locally to the block. And indeed it will, if no one writes an assignment to "foo" above the block. In Smalltalk, you can easily declare a variable local to the block (although one or more older Smalltalk implementations would scope the variable wrongly at runtime; I hope all modern Smalltalk implementations have cleared this up).

01 Practices and the libraries

02 Choice of Selectors

As reflected in the libraries used widely in the respective languages, Smalltalkers make better choices of message selectors than Rubyists make. For example, I like "isNil" better than "nil?", and "asString" better than "to_s" (which means, "to_string"; I'm nitpicking here about the "to" vs. "as" rather than about the abbreviation of "string" as "s"). All the question-mark selectors can be expressed in a more English-like way by using the conventions Smalltalkers have coalesced around, using "is", "includes", or other verbs. "as" selectors express a more functional attitude, and "to" selectors express a more imperative attitude.

02 Array Stores

Smalltalkers choose on the basis of efficiency, whether to store a sequence of values in an OrderedCollection (for the non-Smalltalkers among the readers, note that in Smalltalk, "OrderedCollection" denotes a particular implementation, not a type -- not every ordered collection is an OrderedCollection) or an Array. Rubyists just use the Array type (it's a class, but it might as well be a type). If they need to add to the end of it, they just do.

Smalltalkers use SortedCollection if they want their data sorted, and the class makes sure to keep the instance sorted through its history of mutations (by and large); rubyists just tell the array to sort itself when they want it sorted.

So, in practices around arrays, the Ruby world looks simpler. Programmers face fewer choices, and fewer concepts to learn. You just write what you mean.

01 Private Methods

Ruby enforces declared privacy of methods at runtime. Smalltalk implementations I used do not attempt this. If I recall correctly, Dolphin does. As a Smalltalker, I treated the software-engineering declaration that a method is private, simply with discipline, not looking for enforcement support from the language itself. Ruby backs away from enforcing method privacy when the call is indirect via "send", and taking advantage of this is the only way to do certain useful metaprogramming. I wish the relevant methods were public. Bottom line, maybe others will argue strongly for or against privacy enforcement; I guess it's not a big deal to me.

01 Development Environment

Every Smalltalk implementation except the Gnu one offers a development environment where your data can be present along with your software to be modified. You can set up conditions very quickly and try out code, and examine it in any intermediate state of execution, and modify the code at any point, and so on, as pointed out elsewhere. No Ruby implementation so far offers this. Ruby practices depend on the file system as the repository for the software; Smalltalkers use the image. Either practice may fall back on a different repository for the real code repository for version control and sharing with other programmrs (Subversion, or Envy, or whatever), but I'm talking about the development environment here. Smalltalkers look at their code through the class browser. The source code seems to live in the environment along with the values of variables, etc. The practice with Ruby is to organize the code in a file system. I think Smalltalk has it all over Ruby in this regard. It's great to see the source code of any method instantly, and browse the senders or implementers of a given selector.

01 What the language environments could easily learn from each other

I submit that Ruby could learn the class browser and browse senders and browse implementors and the whole image with the data and software being present at the same time, the easy debugging and incremental tracing and trying code on the fly and changing it on the fly, all that, from Smalltalk. And that Smalltalk could learn to put some macros in the class definitions. Just keep the source code for the class definition (excluding the methods), as one snippet of text (or maybe more that one, if you have a module system where different modules could contribute to the definition of "open" classes), keep that snippet of source code along with the class (or in modules if you go there), make it visible in the browsers, and every time it is touched, execute it again. Some practices would have to be added to both languages so for the generated consequences of running the macro calls in the class definition would not survive when you edit the definition. Yes, there are issues with how that affects existing instances of the classes, but the languages could solve that more or less the way Smalltalk handles schema evolution today. All the metaprogramming that Ruby supports that make those macros work well, plus the modules and mixins, and the adding methods to objects dynamically, I think that those could be added to Smalltalk without disrupting what Smalltalk already is and does. None of these changes would require touching a parser or a compiler in either language environment, nor the virtual machine that runs either language. Such changes fit with the basic class (or "behavior") model shared by the two languages. I suspect I could sit in a Smalltalk image right now and look at how Class and Metaclass and ClassDescription are subclassed off Behavior, go back to basic Behavior, subclass it differently and implement Ruby's modules, mixins, and the attachment of methods to objects, basically the same way Ruby does it. The languages as implemented have no fundamental difference in their virtual machines. Under the hood, Ruby puts all methods in behaviors just as Smalltalk does. She simply gives any object that has custom methods, a special "virtual" behavior (class), which inherits from the object's nominal class. Just as easy to do in Smalltalk.

01 Paid Work

I am glad to see Ruby on the scene, because as a very Smalltalk-like language semantically, with even some advances over Smalltalk both in semantics and syntax, and as a language that judging by the count of ads on Monster and the like, is catching on somewhat in the corporate world as Smalltalk itself dies out, seems as though it might be the rescue vehicle for Smalltalk-like languages as a professional tool. A fine development to tide me over until the whole programming world switches to declarative programming (or dies out due to the end of oil, but that's another subject).

01 Web Support

Ruby has Rails and Smalltalk has Seaside. These are not the same, so I understand.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

Ruby mixins and "macros" in Smalltalk

When I first came to Smalltalk several years ago, my primary language at the time was Ruby, and I latched onto many of the same things you mention here: the syntactic sugar like implicit self and string interpolation didn't bother me much (on balance I'm happier without it), but I really missed things like attr_accessor (what you're calling macros), singleton methods, and the module/mixin system. As you correctly point out, it's not very difficult to implement all of those in the Smalltalk runtime, and I immediately did this. To my surprise, however, although I had used these features frequently in Ruby, I never once used them in Smalltalk. I can't say for certain why not: all I can report with certainty is that, having the option, I chose not to. However, my best guess is that all of these things upset the delicate balance between dynamism and static analysis that make Smalltalk's tool support work so well. That is, by using them I was making the code writing experience better, but I was making the versioning, debugging, and code browsing experiences worse - effectively, I was bringing them down to Ruby's level, and negating a lot of the advantages that I had come to Smalltalk for in the first place.

Welcome to LtU Avi!

Welcome back to LtU Avi!

Surely you mean "Welcome

Surely you mean "Welcome back"

Surely. But this is a new

Surely. But this is a new member account, and the previous account hasn't been used for awhile...

Static Analysis in Smalltalk

Thanks for the report; it's interesting to read the view of one traveling (or having traveled) in the opposite direction as I am between these languages. And I go to Ruby not because I think it is better than Smalltalk, but because there seem to be opportunities to get paid for programming in it (although currently I'm doing volunteer work full time). (Also, as for web support, Ruby has Rails and Smalltalk has Seaside. And I hear that Seaside bases itself on some weird continuation service or something like that. And I don't believe in continuations or control flow; I think everything should work sort of like in ToonTalk, via messages. And when you program in Rails, you can take pretty close to that viewpoint without going far wrong.)

Now when you speak of static analysis in Smalltalk, that pretty much means "Browse Senders" and "Browse Implementors", right? Are you saying that both these browses get defeated when some of the senders and implementors of a given selector are methods generated by metacode and you can't see their source code or where they came from?

I think when it comes to metacode or macros, and when it comes to passing around symbols that get interpreted, there are good practices and there are bad practices. Taking, for example, two strings that mean something, concatenating them together, and interning the result as a symbol, is an example of a class of practices I call "typographical programming" and I strongly oppose. I wonder whether the real cause of the defeat of Browse Senders and Browse Implementors (if indeed that's what you're referring to) is typographical programming, rather than the use of metaprograms per se.

Cross References

First off, those looking for Smalltalk vs. Ruby should see Ruby vs. Smalltalk, which I think you are seeing if you have reached this page.

Second, the topic of Smalltalk and Ruby has come up on the Ruby vs. Python page. I'd like to try to draw to here the discussions from over there that touch on Smalltalk and not Python.

In Comment 17411, "nat" says of Ruby and Python, "Both are slightly dynamic languages but not as much as LISP or Smalltalk (by which I mean that the language itself can be slightly programmed in itself).".

As a Smalltalker turning Ruby user, I see it the other way around, and I cite the calls (macros as some of us have been calling them) common in ruby class definitions, that are executed when the class is read in, and that do metaprogramming such as whipping up methods and adding them to the class (maintainers do not see these whipped-up methods as part of the source code). And nat replied that Smalltalkers do that too these days, so I'm curious as to where they do that in their codebase, e. g., do they put the calls in some "initialize" method on the class side, or what? So I hope to pull that discussion over here because it's about Smalltalk vs. Ruby, not about Python vs. Ruby.

In Comment 39221 in the Ruby vs. Python discussion, Noel mentions Ruby's "yield", which is part of the closure-argument exceptionalism in Ruby that I complain about above. Noel calls it cute. I responded with my objections to the exceptionalism. Nathan de Vries responds to my response, asking if it is really such a problem. I'm not sure what he means by "coupled". Anyway, I want to respond here rather than there, because my response mentions Smalltalk and not Python, so it is getting into Ruby vs. Smalltalk. So here goes.

I just don't see what the exceptionalism brings to the party. Why not just pass the lambda the way Smalltalk and Lisp do (I mean, as a design consideration for Ruby the language and any future languages, not as a practice for a Ruby programmer to consider)?

What I meant by "coupled"

What I meant by "coupled" was this:

def foo(&bar)
  yield
end

foo() {
  # the block is "coupled" to the calling of foo()
}

Versus this:

def foo(bar)
  bar.call
end

block = lambda {
  # the block is defined elsewhere, and can be passed to foo()
}

foo(block)

Like yourself, I'm unsure why calling a method and supplying a block doesn't call 'to_proc' ('&') and pass the proc as the last argument to the method, without the extra '&' syntax in the method definition. One benefit it does have is that you don't need to inspect the method foo() in order to work out if argument bar is being call()'d, yielded, or treated as another type altogether.

Just some points

[[03 String Constructor]]
I agree that Ruby's string constructor is very nice for a programmer as it looks like a string instead of a concatenation of strings which is used for other language: it's much nicer visually.
But it could be even better if it would allow some processing of the variable like in printf format string:
puts "#08X{foo}"; would print foo as an hexadecimal number.

[[02 Assignment operator [cut] erroneously chose to follow Fortran and its kin in using naked "=" for assignment.]]
Either it's a tongue in cheek comment or it's pure flamebait:
':=' vs '=' is a mostly an subjective esthetic issue, so it's not an error to prefer one over the other!
I prefer '=' instead of ':=' as 1) it's shorter! 2) it allows these nice notations: +=, -=, etc.
Sure you could use :+= but it's ugly!

[[02 Returned Value]]
I strongly disagree with you that by default the latest value evaluated should be returned: programmers working on the E language have found that this lead to 'data leaks': programmers ofter forget to add a 'true' at the end and values which weren't supposed to be seen by the caller are then accessible because they happen to be the last one evaluated even though they weren't supposed to be (a big nono for them as they're working with capabilities).

And '^' is short enough that it's not a big deal to add it even when you want to return x+1..

Returned Value

Thanks for pointing out the lesson from the E programmers. It seems very valid. I happen to believe in capabilities. Maybe for future languages with a notion like flow control (some commands get executed and others don't, according to the results of tests), the syntax should be "^" for return at the end of the method, and "return" (or even both, "return ^") for early return (skipping the rest of the commands) so the early return stands out visually.

Or another approach would be to have the method declared at the beginning as to whether it is supposed to produce a side effect only (a "procedure"), or produce a returned value only (a "function"). The syntax could then be tailored for the difference.

effects systems and dynamic typing

Or another approach would be to have the method declared at the beginning as to whether it is supposed to produce a side effect only (a "procedure"), or produce a returned value only (a "function")

Do any LtUers know of any work related to adding effects systems to untyped languages?

programmers working on the E

programmers working on the E language have found that this lead to 'data leaks'

A problem which is largely mitigated with static typing, as the return type of the function is obvious.

A correction for the first point.

This is a great article, and I appreciate all of the thought that's gone into separating out the differences, small as they may be.

But . . . With respect to your first point, Ruby -- and this is something very delicate that Ruby helps teach you about a pure OO system -- does not add methods to individual objects, because only classes store methods, and objects do not. When you dynamically add a method to an object, you are actually adding it to a small class, inserted into the inheritance tree right above the object itself. So it end up looking like


Object > YourDefinedClass > YourDynamicClass > TheObjectInQuestion

Class-based OO is a subset of OO

It's a lesson about class-based OO, not OO as a whole - see Self for a counterexample.

Under the Hood

Yes, when you look under Ruby's hood, you see the reality you're describing, that all methods live on behaviors (if I may use that term as a generalization of Ruby's Classes and Modules, and Smalltalk's Behaviors). Basically, classes and similar things, special objects that the virtual machine looks in to find the methods to execute. Programmers who write programs that modify programs do lift that hood and see what's underneath. But I contend that a useful additional viewpoint from which to describe Ruby is that of the casual programmer, the one who doesn't need to look under the hood. From that programmer's viewpoint, Ruby provides an easy syntax with the straightforward semantics of adding a method to an object like in Self. That is the effect of exercising that syntax for all intents and purposes as seen by that programmer. So, I say both viewpoints have their validity for different purposes, and I say that Ruby has an advantage over Smalltalk for providing the easy way to add a method that only responds in one receiver.

This is yet another example where one language's pattern becomes another language's feature. Smalltalkers who want a single object that executes methods that no other object executes, resort to the Singleton pattern described by the Gang of Four.

Singleton methods

Are the semantics really simpler though? Ruby uses this same notion of "singleton method" for what in Smalltalk would be class-side methods. However, in this case there's potentially more than a single receiver that uses the method, since all subclasses of the class you defined the method on will also inherit it. I think that's easier to understand in terms of metaclasses (confusing as those can be to beginners) than as an inherited singleton-method, and I bet that experienced Ruby programmers reason about it in those "under the hood" terms regardless of what the documentation encourages you to do. But maybe that's just my Smalltalk bias.

Not Just Classes

irb(main):068:0> foo = Object.new
=> #<Object:0x44104c0>
irb(main):069:0> class << foo
irb(main):070:1>   def bar
irb(main):071:2>     "bletch"
irb(main):072:2>   end
irb(main):073:1> end
=> nil
irb(main):074:0> foo.bar
=> "bletch"
irb(main):075:0> Object.new.bar
NoMethodError: undefined method `bar' for #<Object:0x7ff91ecc>
        from (irb):75
        from :0


The above example session from the interactive Ruby interpreter demonstrates attaching a method to an instance of Object. This method fields a "bar" message by answering "bletch". The example goes on to demonstrate that another instance of Object does not field the same selector.

You may have seen examples of Rubyists doing this to classes, which makes much the effect of a class-side method. And somehow, the subclasses do inherit those.