Smalltalk vs C# vs C++ - A generic template story
I got thrown in at the deep end at work recently. I was moved to work on a piece of software which was all written in C++. So I've been spending some time on the dark side.
I'm happy to say that all that power (complexity) has left a bad taste in my mouth. It's made me cling to my Smalltalk even more tightly, however I have to say C++ does have some pretty cool stuff going for it. The "simple" operator overloading (opposed to C#) ans also C++ templates.
I knew what C# generics were before I knew what C++ templates were. Although I knew that generics were based on templates, I'd never actually done anything in C++, so it was a kind of meaningless comment to me.
This post came about when I tried to write a simple Smalltalk like Interval class in C# and generics. I succeeded, but it required me to define two a generic Interval<T> class, two interfaces, one for doing the step addition, and one for doing the comparison between the current and end positions, and two implementations of the Additiona/LessThan operator interfaces. It worked, but damn it wasn't straight forward. I've put the code here if you're interested. I did two versions; One using anonymous methods, and one using interfaces. Interval.cs. This is not the "One True Way" (I could have used an enumerator and yielded each value, a new feature in C# 2.0.. anyways!) .. as you can see from the code, it's crufty. There is a lot of code to write for what should be something quite simple.
Next I wrote up a C++ version. Interval.cpp. Guess what, much simpler!
Finally here is a pseudo-Smalltalk version, because it isn't proper chunk format (I wrote it in notepad) which does the same thing. Interval.st.
One comment on this: I think it is the only time you'll see C++ code that is close to Smalltalk, and has the same requirements on the object the Interval steps over: that the object implements the messages #<= and #+. The issue with C# is that C# CAN NOT use operators with generics. I didn't realise why there was an uproar about this when generics for C# was first announced, but now I understand.
BTW, if you've never actually used Smalltalk before, there is already a very rich and useful Interval class which implements the collection protocol, so you can actually do things like iterate over it using #collect: #select: and so on. Very nice indeed. There is no such equivelent in the static world.
James has made a couple of posts recently on the issues with generics in C# and Java. Every time I start to write any generic code in C#, I wonder why people can make comments about Smalltalk being the train that "rusted at the station", when clearly that's just not possible when the Smalltalk train was made out of nice shiny stainless steel.
Update: Oops. Fixed the broken links
Comments
Specifically?
[Isaac Gouy] November 15, 2005 18:52:51.274
What, specifically, is there no such equivalent to in the static world?
(I'm feeling a little over-awed that someone knows what's going on in every corner of "the static world".)
Really?
[ James Robertson] November 15, 2005 19:38:38.726
Comment by James Robertson
I'm feeling a little over-awed at your seeming need to gainsay everything we say about dynamic typing here. It's like an obsession, or something :)
Heh
[Sean] November 15, 2005 22:01:02.458
"There is no such equivelent in the static world"
I was referring to the Collection protocols.
I happily acknowledge my sweeping generalisation, and it was made with Collection heirarchies available in the other languages that I have had personal experience with in mind. Feel over-awed if you want, or perhaps next time you can actually provide real input. Silly me, how could I expect that of you
Allow me to tighten my statement: In my own minimal experience I have yet to find anything that matches the Smalltalk collection protocols, whose elegance comes from Smalltalks message based design, <bold><blink>IMHO</blink></bold>.
More specifically
[Isaac Gouy] November 17, 2005 3:09:23.345
yet to find anything that matches the Smalltalk collection protocols
Why do you think that is? Do the other languages you know not provide an equivalent to blocks? Do they not provide a common enumeration protocol for arrays, and lists, and...?
void main(String[] args) { for (int i : ( [1, 2, 3, 4, 5].filter( int i => i > 3 ))) println(i); println(); for (int i : ( new ArrayList([1, 2, 3, 4, 5]).filter( int i => i > 2 ))) println(i); } I:\>java -jar t.jar 4 5 3 4 5More specifically
[Isaac Gouy] November 17, 2005 19:13:43.030
Do they not provide a common enumeration protocol for reference types and primitive types?
void main(String[] args) { [1, 2, 3, 4, 5] .filter( int i => i > 3 ).foreach( int i => println(i) ); println(); [new Thing(id:1), new Thing(id:2), new Thing(id:3), new Thing(id:4), new Thing(id:5)] .filter( Thing t => t.id < 3 ).foreach( Thing t => println(t.id) ); } class Thing { int id; } I:\>java -jar t.jar 4 5 1 2Sorry
[Sean M] November 17, 2005 19:53:23.854
"Do the other languages you know not provide an equivalent to blocks?"
Please do give a Java example of using a block/closure? BTW, what version of Java should I be using to compile that code you provided?
no such equivelent in the Java world
[Isaac Gouy] November 18, 2005 13:46:00.146
Please do give a Java example of using a block/closure
Now I understand! When you wrote "There is no such equivelent in the static world" you actually meant "There is no such equivelent in the Java world".
compile that code
It isn't Java - it's one of those many other statically type checked languages.
Yeah...
[Sean] November 20, 2005 23:24:51.589
Hence my comment about generalising.
So tell me, what is the language you're posting snippets from?
Nice, Scala, ...
[Isaac Gouy] November 21, 2005 2:58:58.933
That was Nice.
For someone curious about what is being done with reasonably familiar kinds of programming language more documentation is available for Scala.
Thanks
[Sean] November 21, 2005 6:29:16.968
Thanks for the link
C++ comes very close to blocks
[David Salamon] January 26, 2006 6:30:25.324
The Boost library for c++ (boost.org) has a lambda library. some example code:
#include<iostream> //std::cout #include<vector> //std::vector #include<algorithms> //for_each #include"boost/lambda.hpp" //_1 int main() { std::vector<int> a; for_each(0, 100, _1 = i); // add 0..99 to the list for_each(a.begin(), a.end(), std::cout << _1 << std::endl); // print the list return 0; }this is all done with templates and no preprocessor. for_each takes two iterators and a function pointer.. _1 is an object that returns an object implementing operator() which stands in for that function pointer. Please note that the above is type safe. hope this helps any smalltalkers that think they need to suffer whenever they use C++ :Dcorrection
[David Salamon] January 26, 2006 6:55:11.760
I'm feeling guilty because the above code won't compile.
The problem is that foreach will try an dereference the ints.
use the following foreach instead of the normal <algorithm> one:
template<typename _InIt, typename _Fn> _Fn for_each(const _InIt& first, const _InIt& second, _Fn f) { for(_InIt i(first); i != second; ++i) f(*i); return f; } template<typename _Fn> _Fn for_each(const int first, const int second, _Fn f) { for(int i(first); i != second; ++i) f(i); return f; }