You are viewing pozorvlak

Beware of the Train - If-statements in Smalltalk [entries|archive|friends|userinfo]
pozorvlak

[ website | My Website ]
[ userinfo | livejournal userinfo ]
[ archive | journal archive ]

Links
[Links:| My moblog Hypothetical, the place to be My (fairly feeble) website ]

If-statements in Smalltalk [Mar. 3rd, 2008|01:32 pm]
Previous Entry Share Next Entry
[Tags|, , , , , ]

I did warn you...

A while back, 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"
else
  puts "a is greater than or equal to b"
end
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 (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.
linkReply

Comments:
[User Picture]From: necaris
2008-03-03 03:57 pm (UTC)

(Link)

what other languages do with special syntax, Smalltalk does as a special case of method-dispatch

That is so immensely cool!!!
[User Picture]From: pozorvlak
2008-03-03 05:16 pm (UTC)

(Link)

Isn't it? Haskell does kinda the same thing: if is an ordinary function, built using the language's pattern-matching syntax. Which, if you screw your eyes up just right, is essentially an inside-out version of method dispatch :-)

Common Lisp, IIRC, has a case-statement built-in (or rather, provided as a built-in function, called cond), and then if is implemented as a macro that expands to a call to cond.
From: (Anonymous)
2008-03-03 05:50 pm (UTC)

Sorry, that's incorrect

(Link)

Haskell's if statement is not an ordinary function. Lazy evaluation lets you write a function that works like an if statement:
data Then = Then
data Else = Else

if' :: Bool -> Then -> a -> Else -> a -> a
if' True Then t Else f = t
if' False Then t Else f = f

But that's not how it's done in standard Haskell. "if", "then", and "else" are reserved words.
[User Picture]From: pozorvlak
2008-03-03 05:55 pm (UTC)

Re: Sorry, that's incorrect

(Link)

Bah. Thanks for the correction! BTW, I like the "nullary type constructors as pseudo-keywords" trick - I don't think I'd seen that before.
From: fusiongyro
2008-03-05 08:21 am (UTC)

(Link)

Technically, cond is a special form because it survives compilation but doesn't evaluate its subforms strictly. All the special forms are provided as built-ins; users can only provide functions (which survive compilation but evaluate strictly) and macros (which are expanded at compile-time but may not evaluate strictly).

(Hopefully I got that right in spite of being a bit tipsy.)
[User Picture]From: pozorvlak
2008-03-05 08:51 am (UTC)

(Link)

Ah, right - yes, of course it would have to work like that. I was unclear on the distinction between the three - thanks!
[User Picture]From: livemerlyn
2008-03-03 04:19 pm (UTC)

smalltalk: simple and elegant

(Link)

Yes, since first seeing Smalltalk in 1980, I was very impressed at how a simple syntax (although only slightly more complex than LISP) could hit just the right combination of expressiveness: that magic sweet spot for an extensible language. The only sane alternative is the huffman-compressed Perl, where the designers have to think of every way people would want to use syntax, and build it into the language with shortcuts for the common items. This is great for experienced people, and horrible for beginners, and horrible for people who want to extend the language.
[User Picture]From: pozorvlak
2008-03-03 05:13 pm (UTC)

Re: smalltalk: simple and elegant

(Link)

Yep. I have huge amounts of appreciation for the amount of work and attention the Perl maintainers have put into the language to make things work as well as they do.

I take it you know about Paul Graham's Arc? He seems to be trying the same thing as the Smalltalkers, albeit starting with a Lisp: trying to evolve a minimalist core that can be extended as far as possible. I'll be interested to see how well he succeeds.

[By the way, how did you find this blog?]
[User Picture]From: livemerlyn
2008-03-03 05:16 pm (UTC)

Re: smalltalk: simple and elegant

(Link)

Found it via http://www.cincomsmalltalk.com/blog/blogView?showComments=true&printTitle=Why_Part_of_the_Library_Matters&entry=3381991874. And yes, I've seen Arc, and it has all the ugliness and crypticness of Scheme and Lisp. Ugh. I prefer nice typeable keywords, thank you.
[User Picture]From: pozorvlak
2008-03-03 05:48 pm (UTC)

Re: smalltalk: simple and elegant

(Link)

Wow, not the reaction I'd expected :-)
From: (Anonymous)
2008-03-03 06:12 pm (UTC)

Dynamic Object systems

(Link)

just read http://astares.blogspot.com/2008/02/who-owns-language.html (http://astares.blogspot.com/2008/02/who-owns-language.html) or
http://astares.blogspot.com/2005/06/is-eclipse-next-computer-revolution.html (http://astares.blogspot.com/2005/06/is-eclipse-next-computer-revolution.html)
[User Picture]From: pozorvlak
2008-03-03 10:13 pm (UTC)

Re: Dynamic Object systems

(Link)

Er, yes, that's kinda what I was trying to say :-) Thanks!
From: mernen.myopenid.com
2008-03-03 07:22 pm (UTC)

(Link)

Nit: the Ruby version is more precisely described as:
if a < b then
  "a is less than b"
else
  "a is greater than or equal to b"
end
There's no puts (or Transcript show:) in the Smalltalk version, and Ruby has no problem returning values from if statements, resulting in something quite close to the Smalltalk version.
From: (Anonymous)
2008-03-03 08:28 pm (UTC)

Another nit

(Link)

ifTrue:ifFalse: is a single method, not two methods. However, True and False can be made single members of different classes - with ifTrue:ifFalse: overridden in each of them to only execute the right chunk of code, without actually doing any runtime testing at all; the condition is resolved as a byproduct of method dispatch.

For example, True can define ifTrue:ifFalse: as

    ifTrue: t ifFalse: f
        ^t value


while False can define it as

    ifTrue: t ifFalse: f
        ^f value
[User Picture]From: pozorvlak
2008-03-03 10:03 pm (UTC)

Re: Another nit

(Link)

Ah, that makes more sense - I'd been wondering how the method chaining worked. So Smalltalk can define method signatures with noise in, like TeX?

As should be obvious, I don't know very much about Smalltalk (though I'm interested, and would like to know more), and I don't, come to that, know much about Ruby either - the code samples were yoincked wholesale from this page, which I found by Googling for "Smalltalk if statement" or some such. But anyway, thanks for your corrections!
From: (Anonymous)
2008-03-03 07:52 pm (UTC)

(Link)

you need to take a look at Io, boy.
[User Picture]From: pozorvlak
2008-03-03 10:04 pm (UTC)

(Link)

Oh, probably, but there are so many cool languages out there, and I've got a thesis to write :-)
[User Picture]From: livemerlyn
2008-10-09 03:50 pm (UTC)

Io made the loose-boolean mistake

(Link)

I started to look at Io, and then I realized they made the "everything is a boolean" mistake. Having played with Smalltalk, I'm very happy now that I can't accidentally ask "is this true" to a non-boolean in Smalltalk. Yes, imagine that, a Perl hacker wanting strict booleans. :)
[User Picture]From: pozorvlak
2008-10-12 02:18 pm (UTC)

Re: Io made the loose-boolean mistake

(Link)

Why is that a mistake? It seems to be one of the more useful cases of type-coercion to me.
From: (Anonymous)
2008-03-03 08:43 pm (UTC)

Kinda

(Link)

what other languages do with special syntax, Smalltalk does as a special case of method-dispatch

No. Or rather, yes, but not the way you mean. Uses of Boolean>>ifTrue:ifFalse: and its relatives look exactly like regular old message sends, but the method selection is highly optimized by the VM (this is true of a few other things too, such as integer arithmetic). You can almost never tell.

The important point is that (barring this optimization, except not that, either, in many implementations with open VMs) the writers of the "library" found in the standard image of your Smalltalk have no particularly special capabilities that an application programmer doesn't have. Which means that writing Smalltalk applications is no different from extending the "language". Which means that writing DSLs is all there is to Smalltalk programming (http://peripateticaxiom.blogspot.com/2006/04/keywords-magic-and-edsls.html).

[User Picture]From: pozorvlak
2008-03-03 10:12 pm (UTC)

Re: Kinda

(Link)

OK, that makes sense. Rather like Scheme's banning iteration constructs, but then requiring tail-call elimination :-) And that's a great post - thanks!
[User Picture]From: pozorvlak
2008-03-03 10:16 pm (UTC)

Re: Kinda

(Link)

It's interesting - I hear the claim "Language X is the best thing for writing DSLs!" for (at least) X in {Smalltalk, Ruby, Lisp, Haskell, APL} - all very interesting languages in their own way, and all with very different approaches to DSL creation! It would be interesting to really learn them all, and compare: unfortunately, the cognitive load of learning even one properly is rather high.
[User Picture]From: pozorvlak
2008-03-03 10:16 pm (UTC)

Re: Kinda

(Link)

OK, I just used the word "interesting" three times in two sentences.

That's quite an accurate representation of my mental processes, actually.
From: (Anonymous)
2008-03-03 10:55 pm (UTC)

Re: Kinda

(Link)

Not sure that I'd claim that Smalltalk is better than, say, Lisp for writing DSLs. I would claim that Smalltalk is the one that most encourages (partly, by example) the writing of them. But perhaps only slightly more than Lisp. the others I can't speak to.

Of course, only Prolog comes with a first-class facility for writing actual interpreters both trivially and right out of the box. :-)
[User Picture]From: pozorvlak
2008-03-04 12:13 am (UTC)

Re: Kinda

(Link)

Yeah, writing new interpreters seems to be about 90% of intermediate-to-advanced Prolog programming ;-)

As for Lisp and language-extension, check out this implementation of Haskell-style pattern matching in Arc :-)
[User Picture]From: thesz
2008-03-03 09:28 pm (UTC)

(Link)

Lambda-calculus (true = \a b -> a), (false = \ a b -> b) and (if = \c a b -> c a b).
[User Picture]From: thesz
2008-03-03 09:32 pm (UTC)

(Link)

And, by the way, I'd like to see how they did it in the internals.

While syntax sugar around lambda calculus like Haskell is allows us to use almost all LC tricks, I don't think this is just as easy (or even possible) with Smalltalk.
From: (Anonymous)
2008-03-04 12:49 am (UTC)

(Link)

Blocks in Smalltalk are equivalent to lambda expressions, so you can certainly write the same things, whether its as cute is a matter of personal choice :). I'd take true := [ :x :y | x ] over (true = \a b -> a)
[User Picture]From: thesz
2008-03-04 01:08 am (UTC)

(Link)

Okay.

But Smalltalk lacks much more than cute lambda syntax. It lacks lazy evaluation, type inference, inductive data types, pattern-matching... And while syntax of St can be hammered into something like DSEL, it certainly does not allow DSELs to be built quickly and reliably.

This is matter of personal choice, of course. I prefer to use simple editor to work on hard problems.
[User Picture]From: kragen
2008-04-16 11:56 am (UTC)

(Link)

It lacks implicit lazy evaluation, but you can put [] around any expression to keep it from being evaluated until needed. It certainly does have inductive data types; both MVC and Morphic, not to mention most of the compiler, are built out of them. (Although they sometimes call it the "Composite Pattern" instead.) It doesn't have type inference, but that's okay, because it's dynamically typed, so the lack of type inference doesn't cost you expressiveness; it just costs compile-time error checking and speed.

However, I do wish it had pattern-matching!

Your statement that "it certainly does not allow DSELs to be built quickly and reliably" does not agree with my limited experience. Maybe you can elaborate on your experience building DSELs in Smalltalk and the difficulties you've had? You must have quite a bit of experience at this to justify such a strong statement.

About simple editors and hard problems: have you tried using a pencil and paper instead of a simple editor? I find that they often work better, especially if I hvae someone else to talk with.
[User Picture]From: thesz
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.
[User Picture]From: pozorvlak
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.
[User Picture]From: thesz
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... ;)
[User Picture]From: kragen
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.
[User Picture]From: thesz
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.
[User Picture]From: kragen
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 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 or the LtU discussion of DSELs in Smalltalk. Or Adam Turoff's offhand assertion 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.
[User Picture]From: thesz
2008-04-23 08:03 am (UTC)

(Link)

Yes, I am certain about relative stability of EDSLs.

I think, it sums it all.
[User Picture]From: pozorvlak
2008-04-23 11:09 am (UTC)

(Link)

Hey, great links, thanks! I look forward to reading those.

I'm sorry 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...
[User Picture]From: thesz
2008-04-16 12:13 pm (UTC)

(Link)

But, even poor Tcl did us a favor. We made substantially more in noticeable less time.

So don't get me wrong. I think that any DS(E)L is better than just a library. But more DSL is better and simpler DSL construction is even better too.

That's my point.
From: (Anonymous)
2008-03-04 01:41 am (UTC)

Impossible tricks

(Link)

What specific "tricks" do you have in mind?
[User Picture]From: thesz
2008-03-04 01:49 am (UTC)

Re: Impossible tricks

(Link)

Control operators, construction and deconstruction of (possible infinite) data, etc.
From: FrankShearar [typekey.com]
2012-08-10 12:51 pm (UTC)

(Link)

Control operators are trivial in Smalltalk (implemented here (http://ss3.gemstone.com/ss/Control.html), for example), since almost all implementations give you direct access to the current activation context (and hence to the entire call stack). In fact, Smalltalk has the _opposite_ problem: it's difficult to _reduce_ the abilities of the programmer to manipulate the stack.
[User Picture]From: thesz
2012-08-10 02:28 pm (UTC)

(Link)

How good are the compositional properties of those operators?
[User Picture]From: thesz
2008-03-04 01:51 am (UTC)

Re: Impossible tricks

(Link)

And, actually, I do not have them in mind. I use them almost on daily basis. ;)
From: (Anonymous)
2009-06-23 04:36 am (UTC)

Factor

(Link)

! if is in the library in factor also it is defined in terms of the ? word ! like this ! do not actually write this : my-if ( ? if-true if-false -- ) ? call ; ! of course you can define it the other way if you ! wanted to do it like smalltalk GENERIC: (??) ( t f ? ) M: t (??) 2drop ; M: f (??) drop nip ; : ?? ( ? tval fval -- either ) rot (??) ;
From: (Anonymous)
2009-06-23 04:38 am (UTC)

Re: Factor

(Link)

Factor actualy has f be a special false value rather than do it through method dispatch.