pozorvlak: (gasmask)
pozorvlak ([personal profile] pozorvlak) wrote2008-03-03 01:32 pm

If-statements in Smalltalk

I did warn you...

A while back, [livejournal.com profile] markdominus wrote an article about the Design Patterns movement, in which he claimed that design patterns are basically signs of weaknesses in programming languages; if a language forces you to implement the same pattern of code over and over in different programs, that's a sign that your language is insufficiently high-level with respect to problems that your users actually encounter, so language designers should focus on patterns as places where their languages could be improved. This prompted some reaction in the tech blogosphere, including this response from Ralph Johnson, one of the authors of the original Design Patterns book. Dominus responded here, correcting some of Johnson's misunderstandings and commenting further; in fact, though, they're mostly in agreement (and I'm pretty much in agreement with them both, FWIW).

Everyone up to speed? Good.

Something Dominus didn't seem to get was Johnson's distinction between things that are in the language and things that are in the library. Johnson seems to think that there's a three-tier hierarchy:

1) Language features
2) Library code (available to every application)
3) User code (must be written for each application).

He claims that it can be a good tradeoff to leave something as a pattern, because the alternative might be to overcomplicate the standard library or, even worse, the language itself - he seems to have visions of a language with decorator, abstractFactory etc. as keywords. Dominus largely refutes this claim in his response. But more interestingly, Dominus thinks that the distinction between 1 and 2 is basically artificial - is C's printf (or Haskell's standard prelude) part of the language or the library? It might matter to language implementers, but to the average programmer, the distinction's pretty unimportant. Now, this is a good point, but I think I can see what Johnson's getting at. Remember that Johnson's a Smalltalker. Let's consider (finally) how Smalltalk handles if-statements, which in most languages are built-in syntactic elements of the language.

In Smalltalk, booleans (ie, True or False) are objects: specifically, they're instantiations of the abstract base class Boolean, or rather of its two subclasses True and False. So every boolean has type True or False, and no actual member data. Bool has two virtual functions, ifTrue: and ifFalse:, which take as their argument a block of code. Both True and False override these functions; True's version of ifTrue: calls the code it's passed, and False's version does nothing (and vice-versa for ifFalse:). Here's an example:
a < b
  ifTrue: [^'a is less than b']
  ifFalse: [^'a is greater than or equal to b']
Those things in square brackets are essentially anonymous functions, by the way. Except they're objects, because everything is an object in Smalltalk. Now, what's happening there is that we call a's "<" method, with argument b; this returns a boolean. We call its ifTrue: and ifFalse: methods, passing as arguments the code we want executed in either case. The effect is the same as that of the Ruby code
if a < b then
  puts "a is less than b"
  puts "a is greater than or equal to b"
but what other languages do with special syntax, Smalltalk does as a special case of method-dispatch. (Code samples from here). This completely blew me away when someone ([livejournal.com profile] pdcawley?) first told me about it. Haskell does something similar, of course. [Edit: no it doesn't - see comments. But it could. I've also got some details of the Smalltalk wrong.]

Now, back to Johnson. Smalltalk's designers had three choices: bake special syntax for if-statements into the language, implement it in Smalltalk library code in terms of more general concepts that were part of the language, or force the programmer to do the work him/herself every time (using, I dunno, computed gotos or something). They made the unusual choice to go for the second option. While this doesn't matter if all you want are if-statements, it affects the language in other ways: the powerful abstraction mechanism needed to do this is available to the user to define new features.

This, I think, is the real distinction between "language" and "library": if the feature in question could have been implemented by your users using a combination of other features, it might as well be part of the library. Smalltalk's if-statements pass this test, as do (say) Lisp's looping constructs. Haskell's monads fail, because of do-notation: if a Haskell programmer wanted to add similar syntax for (say) comonads, would they be able to? I don't think so, and so it follows that monads are actually baked into the language. Several of Perl's built-in functions do things that user functions wouldn't be able to do, and thus should count as part of the language, even though they feel like part of a library.

So here's where it applies to design patterns: if your programmers find themselves having to implement essentially the same code over and over again, it's because you don't provide powerful enough abstraction mechanisms for programmers to write library modules that would do the job for them. Programmers are (rightly) lazy, and don't do work many times if they can do it once and then use that solution over and over again. So the fact that it's a design pattern means that it can't be in the library, and your language needs fixing: either by special-casing extra language features in or (preferably) by making your abstraction features more powerful, so the necessary library code can be written.

[identity profile] thesz.livejournal.com 2008-04-16 12:09 pm (UTC)(link)
That was not Smalltalk, that was Tcl. And it was testingtestingtestingtesting. And strict execution order and no combinators and maybe something else I already forget. I don't think that Smalltalk can cut most of the problems.

And that's why I am already in Haskell camp and looking into dependent types.

[identity profile] pozorvlak.livejournal.com 2008-04-16 06:03 pm (UTC)(link)
Ah. Not all dynamic languages are created equal. In particular, Tcl is generally held to be a lot less powerful and elegant than Smalltalk.

[identity profile] thesz.livejournal.com 2008-04-16 06:32 pm (UTC)(link)
Not in DSEL area. ;)

I did an approximation of algebraic types and pattern matching in Tcl, when I decided I need one: http://wiki.tcl.tk/9547

The speaker above complains St does not have pattern matching, yet praising DSEL abilities of St.

So... ;)

[identity profile] kragen.livejournal.com 2008-04-18 03:55 am (UTC)(link)
Unlike Tcl, Smalltalk has garbage collection, closures, and dynamic dispatch (extending to operator overloading), all of which help a lot in building EDSLs, in my limited experience. It's flexible enough that you can fake pattern-matching to some extent, and its syntax (while less minimal than Tcl's) gets in the way less than the syntax in C-family languages when you're trying to EDSL around.

I guess I shouldn't spend too much time arguing with you; you were already certain before you had any experience. So you're probably not the kind of person to change your mind because of little things like other people's experiences.

[identity profile] thesz.livejournal.com 2008-04-18 08:06 am (UTC)(link)
Like we Russian say "Oba-na!" ("Oba-na" is exclamation of extreme wonder)

I had an experience, I asked others about their experience and I, actually, shaped my mind on all that combined.

So, yes, single person experience (read: anecdotal) probably won't change my mind. Even if that person tries to present me as rigid unexperienced (insert even more offensive definition) kind of developer.

I cannot brag that I put a DSEL into every program I write, but here it is: http://thesz.mskhug.ru/browser/hiersort/sim/Assembler.hs

An assembler for dynamic dataflow machine prototype. It comes up so easy and naturally that I think it was the only way to implement program abstraction for processors. That's what I am accustomed to.

[identity profile] kragen.livejournal.com 2008-04-23 07:46 am (UTC)(link)
The dataflow machine project is pretty interesting. After I read your paper, I followed some of the references in it and ended up reading all this old literature I'd never known about, including my friend Ellen's dissertation!, and then wrote a little large-granularity dataflow machine simulator in Python. Thanks!

Unfortunately I still don't understand very much of, say, MatMulProg.hs, partly because my Haskell isn't very good.

It sounded to me like you made up your mind --- with certainty --- without having any experience doing EDSLs in Smalltalk yourself, or even talking to anybody with experience doing that --- reasoning only from analogy with Tcl. Do you really think that a person who made up their mind in such a way would have a better-than-random chance of being correct?

I don't think my own experience with the language is really relevant; I've only written a tiny amount of Smalltalk. But take a look at Avi Bryant's Roe, for example, compare it to HaskellDB, and see what you think. Or read Martin Fowler's comments (http://www.martinfowler.com/bliki/DomainSpecificLanguage.html) about how he never feels the need for non-embedded DSLs in Smalltalk, or his post about how this is one of Smalltalk's major strengths (http://www.martinfowler.com/articles/languageWorkbench.html) or the LtU discussion of DSELs in Smalltalk (http://lambda-the-ultimate.org/node/2512). Or Adam Turoff's offhand assertion (http://notes-on-haskell.blogspot.com/2007/08/does-syntax-matter.html) that Smalltalk doesn't need non-embedded DSLs: "Lisp and Smalltalk programmers might honestly count one single syntax for all their work. :-)" Or some other examples of EDSLs in Smalltalk. (It's hard to find references for them because they seem to be so invisible.)

But you already said you were certain about Smalltalk's suitability for implementing EDSLs, and so I probably shouldn't waste my time telling you that other people seem to think differently, or that I've seen examples that look pretty impressive.

[identity profile] thesz.livejournal.com 2008-04-23 08:03 am (UTC)(link)
Yes, I am certain about relative stability of EDSLs.

I think, it sums it all.

[identity profile] pozorvlak.livejournal.com 2008-04-23 11:09 am (UTC)(link)
Hey, great links, thanks! I look forward to reading those.

I'm sorry [livejournal.com profile] thesz is being so obstinate - usually he's pretty good value, although perhaps excessively wedded to the Haskell way of doing things :-) Actually, that's one of my problems with the Haskell community in general - all the people who think that the Haskell Way is the One True Way. I get the impression that the Smalltalk community has a few people like that too, though...