Send to Printer

smalltalk

Simplicity

April 20, 2006 8:50:18.508

The other day, I pushed up a post on the simplicity and power of Smalltalk, using the #factorial method in class SmallInteger as an example. Here's the entire implementation within the superclass, Integer:

 

factorial
	| tmp |
	self < 0
	       ifTrue: [^self class
	               raise: #domainErrorSignal
	               receiver: self
	               selector: #factorial
	               errorString: (#errFactorialInvalid <<
#dialogs >> 'Factorial is invalid on negative numbers')].
	tmp := 1.
	2 to: self do: [:i | tmp := tmp * i].
	^tmp


So ignoring the error handling, the basic code is 3 lines. In one place. Sure, it relies on the ability of SmallInteger objects to auto-promote themselves - but the point is, as a developer, I don't need to worry about those details - it just works, and gives me the right answer. I also made the point that developers can create code that has the same kind of power - a quick look at #become: will explain what I mean. It's not a method to be used lightly, but - when you need it, it's invaluable.

If I wanted to write #factorial in Java, would I be able to write one method in one place? Would the issues of correct type get in the way, and force me to consider utterly irrelevant implementation details? That was the point I was making. The fewer "make the compiler happy" issues I need to deal with, the better off I am. At the end of the day, the compiler isn't my customer.

Update: Here's a Java implementation. Sadly, it won't work with numbers bigger than 20. Kind of makes my point, I think.

Comments

[Jules] April 20, 2006 10:10:52.000

In Haskell, it's even better (without errorhandling):

fac n = product [1..n]

 

(with errorhandling):

 fac n | n > 0 = product [1..n]

 

There are many languages that don't have issues with smallints and largeints, Haskell handles them automatically, for example. Ruby does too, but the problem is that they're not "mainstream".

I really like the way Smalltalk handles ifTrue/ifFalse with closures/code blocks, where Ruby has a keyword for that.

[] April 20, 2006 12:32:37.000

Sure, in something like C++ you have to declare that you want to use your favorite arbitrary precision integer library, if that's what you desire. I thought Smalltalk fans would be all for that. You know, using libraries instead of adding language features.

template <class T> T fact(T n){
    T f = 1;
    if(n<0) throw std::invalid_argument("arg to fact negative");
    for(; n>1; f*=n, --n);
    return f;
}

[] April 20, 2006 12:44:21.000

Oh, and your non-Javascript comment entry box has a but. It apparently converts a character entity like "&lt;" into literal "<" upon preview, which subsequently gets eaten after further previews. So the first line of the function above should really be...
template <class T> T fact(T n){

[] April 20, 2006 12:56:49.000

Arg. It has a "BUG", I guess that's what happens when the preview feature doesn't work correctly. I guess it's slightly better than the Javascript editor, where there's no preview available at all. And do you really get that much spam that you need have rate limited posting?

There was a server error accepting your comment. You must leave at least 300 seconds between comment attempts.

Re: Simplicity

[ James Robertson] April 20, 2006 13:12:24.000

Comment by James Robertson

Yes, I get a lot of spam attempts. I look at my logs periodically, and if I didn't have a throttle, I'd get hosed.

Another annoyance with the Java implementation

[ Troy Brumley] April 20, 2006 13:20:33.000

Comment by Troy Brumley

As written, the Java implementation is a bit like putting lipstick on a pig. While I prefer the recursive nature of the Java implementation from an esthetic view, I don't like that it repeats the three guard clauses every time through in addition to the overhead of recursion. There should either be a private factorial method which counts on the guard clauses in the public factorial method, or it should go through the repeated multiplication as the Cincom Smalltalk version does.

fee fie foe fum I smell a java strawman

[Isaac Gouy] April 20, 2006 13:37:33.000

As before, here's the entire SML implementation shown on Wikipedia

  fun fact n : IntInf.int =
      if n=0 then 1 else n * fact(n - 1)

a quick look at #become: will explain what I mean
How so? The factorial example demonstrates arbitrary precision arithmetic on numbers. Numbers are immutable:
   5 become: 10000

Unhandled exception: cannot use become: on immutable objects
SmallInteger(Object)>>error:
SmallInteger(Object)>>handleFailedBecome:
SmallInteger(Object)>>primBecome:
SmallInteger(Object)>>become:

would I be able to write one method in one place?
This Smalltalk implementation only provides arbitrary precision integer arithmetic - even in Java we can write one method in one place if we only ever use arbitrary precision integer arithmetic.
  public static BigInteger factorial(BigInteger n) {
    return n.compareTo(BigInteger.ONE) <= 0 ?
       BigInteger.ONE :
       n.multiply( factorial(n.subtract(BigInteger.ONE)) ); }

blame Princeton CS

[Isaac Gouy] April 20, 2006 13:44:56.000

Troy wrote I don't like that it repeats the three guard clauses every time... as the Cincom Smalltalk version does
The Smalltalk example code is part of Cincom's product, the Java example code is something James' grabbed off the web - it isn't anything to do with Java the language or libraries.

So, can you show us the real implementation?

[ Troy Brumley] April 20, 2006 14:48:19.430

Comment by Troy Brumley

In Smalltalk, we can see the library implementation. In Java, I don't know. My Mac is at home, and I don't have a Java environment on this PC, so I can't go looking to figure out what might be in the Java libraries. Google returned no items for my search "java.math.factorial source" while Yahoo! found quite a few hits, but nothing I felt was pertinent.

So, Isaac, can you delve into the library?

what might be in the Java libraries

[Isaac Gouy] April 20, 2006 15:04:14.460

afaik the easiest way is to look at the JavaTM API Specification index.

afaik after a decade they still haven't found a reason to add a factorial method to the standard libraries - maybe a coalition of university professors lobbied to prevent them ruining their first term class assignment :-)

[] April 20, 2006 15:10:24.801

If it is going to be part of the standard library, then you'd probably want to implement it with a good algorithm.

[Daniel Berger] April 20, 2006 15:56:47.820

If this is Smalltalk's idea of simplicity, I'll be sticking with Ruby, thanks.

naive factorial in Smalltalk

[Isaac Gouy] April 20, 2006 16:50:03.974

Daniel, we could write a naive factorial in Smalltalk like this

fact
   ^(self <= 1) ifTrue: [1] ifFalse: [(self - 1) fact * self]

[] April 20, 2006 18:50:35.679

Just for the heck of it, in Ruby...

def fac(n) n<1 ? 1 : n * fac(n-1) end

instance method

[Isaac Gouy] April 20, 2006 20:51:56.313

Would this correspond to the Smalltalk?

def fac self<1 ? 1 : self * (self-1).fac end

 Share Tweet This