C# (1.x) and Smalltalk case study #1

As promised, here is the first case study I did comparing the “stylings” of C# and Smalltalk. I used C# 1.0 code on the .Net CLR 1.0/1.1. I set up a programming problem for myself, creating a business object, and looked at how well each language handled it. I was inspired to do this by Hacknot’s “Invasion of the Dynamic Language Weenies,” (it has to be viewed as a series of slides) where he said there was no discernable difference in lines of code used in a static vs. a dynamic language, nor were there any advantages in programmer productivity (my response to his article is here). I figured I’d give it my own shot and see if he was right. Up to that point I hadn’t bothered to question my impressions.

What follows is the programming problem I set up for myself:

Create a program that records name, home address, work phone, home phone, salary, department, and employment class of employees. The program must store this information in some fashion (like a data structure).

On input, it must validate the department and employment class of employees when their information is entered. The departments are as follows:

– Manufacturing
– Sales
– Accounting
– Board

The employment classes are as follows:

– Employee
– MidLevel <- Management designation
– Executive <- Management designation

Workers with “Employee” or “MidLevel” class designations can be placed in Manufacturing, Sales, or Accounting departments. They cannot be placed in the Board department. Executives can only be placed in the Board department.

The program will restrict access to the employee information by the following criteria. “all access” means all employment classes may see the information:

name – all access
home address – MidLevel and Executive only
work phone – all access
home phone – MidLevel and Executive only
salary – Executive only
department – all access
class – all access

No elaborate keys are necessary. For the purposes of this exercise using the labels “Employee”, “MidLevel”, and “Executive” as “access tokens” will be sufficient evidence for the information store that the user is of that employment class, and can have commensurate access to the data.

==========================

Edit 10/18/2017: Here are the two versions of the project. I use DropBox to host the files. DropBox will say that the files cannot be previewed, but will display a blue “Download” button. Press that to download each file:

C#/.Net 1.x Employee test

Smalltalk Employee test

The C# zip file contains the test project I used.

The Smalltalk zip file contains the “file out” of the Employees-Test package I created, the test code (as a separate file), and the test output I got. It contains two sets of these files. The files in the main directory have been converted to use CR-LF for easy reading (by you) on PCs. They have the suffix “.txt”. The Squeak folder contains the files in their original format, which use LF as the end-of-line character This will be human readable on Unix-compatible systems, and within the Squeak environment. They have the suffix “.text” or “.st”.

For those not familiar with Squeak, the “file out” operation causes Squeak to create a single text file that contains the source code for a package, plus its metadata. A package is a collection of classes. The only code that a programmer would normally see in the Squeak environment are the class definitions, class comments, the method names and parameters, and the method code and comments. There’s some other code and comments that Squeak puts out when it does its file out operation. This is done so that when the code is read by Squeak (or some other Smalltalk system) in what’s called a “file in” operation, it can reconstitute the classes and their methods as they were intended. Normally code is accessed by the programmer strictly via. a “code browser”, which is the equivalent of a developer IDE within the Squeak environment. All classes in the whole system, including this package, are accessible via. a code browser. There’s no need to look up code via. directories and filenames. Code is accessed via. package name, category, class, and method.

The code that carries out the test for the Smalltalk version is included in a separate file in the zip, called “Employee test data.text” in the Squeak folder (“Employee test data.txt” if you want to display it on the screen on a PC). The test code for the C# version is included in main.cs in the C# project zip file.

I timed myself on each version, evaluated how many lines of code (LOC) it took to accomplish the task in each one, and I looked at code readability. I’ll comment on the other factors, but I’ll leave it up to you to judge which is more readable.

As I said earlier, the Smalltalk version came out slightly ahead in using fewer lines of code, despite the fact that a little more code had to be written for it to accomplish the effect you get with enumerations in C# (validating that a symbol–rough equivalent to an enumeration–is in the set of legal symbols). To keep things fair between them I did not count comments, or lines of code taken by curly (C#) or square (Smalltalk) braces. I also did not count test code, just the code for the business object and helper classes. Within these parameters I counted every line that was broken by an end of line character.

I wrote the C# version first. It came to 75 LOC, and took me 125 minutes to code, debug, and test.

The Smalltalk version came to 70 LOC, and took me 85 minutes to code, debug, and test. Another way of looking at it is 93% the LOC of the C# version, but I wrote it in 68% of the time.

A possible factor in the shorter time it took with the Smalltalk version is I wrote the solution first in C#. I can’t deny that doing it once already probably factored into the shorter time. I made a point, however, of not looking at the C# code at all while I was writing the Smalltalk version, and I wrote them on different days. Though I didn’t break it down, my guess is that debugging the code may have taken longer in the C# version. Since I haven’t worked in .Net for a while I’m a bit rusty. Despite having worked out the basic scheme of how to do it the first time, I was writing it in a different language. So I don’t chalk it up to me just repeating what I had already written.

This is my first crack at doing this. I hope to present more test cases in the future. Since I consider Smalltalk worth evaluating for real world uses, I think it should be evaluated seriously, which means taking measurements like this. Though this isn’t scientific, I’m doing this for me, to check my own perceptions. I just thought I’d share it with the reading audience as well. Something I’ll try in the future is writing a test case in Smalltalk first, and then in C#, and I’ll see if that has an impact on how much time it takes me with each. What I may also do is do future C# test cases in C#/.Net 2.0, just to be more current.

If you’d like to try out my sample projects:

I explain below how to run the Smalltalk and C# tests. Smalltalk first.

I wrote and tested my Smalltalk code using Squeak 3.8.1, from Ramon Leon’s site, onsmalltalk.com. I’ve tested it on the standard Squeak 3.8 version as well (which I got from squeak.org), and it works. Ramon has updated his public version of Squeak to 3.9, which still works with my Employee test package. So you can use any of these versions.

The best thing to do would be to unzip the Employee test files into the same directory as where you installed Squeak. My instructions below will assume you have done this.

Here’s a little Squeak primer. For those who are new, you’ll need this to try out the Employee test. These instructions will work on PCs and Unix/Linux machines. For Mac owners, I’m not sure how this would work for you. My guess is clicking the single mouse button would be equivalent to left-clicking.

When you run Squeak you will see a new desktop interface appear. It’s inside an application window frame supplied by your operating system. As long as your mouse stays inside this window, and the window has focus, most of your mouse and keyboard actions will be directed at objects and activities in the Squeak desktop space.

What follows are instructions for helper applications which you’ll be asked to open in the Employee test instructions (below), to load the necessary files, and execute the test.

File list – This is kind of like Windows Explorer. You access it by left-clicking on the Squeak desktop. This brings up what’s called the “World menu”. Within this menu select “Open…”. A second menu will appear. From it, select “file list”. You will see a new window open, which is the File list app. It has a top row which contains a text box on the left where filter expressions go, and then there are a series of buttons. Below this are 3 panes. The middle-left pane is the directory tree of the drive you’re running Squeak on. The middle-right pane is a listing of all the files in the currently selected directory. The lower, 3rd pane contains the text of whatever file is selected in the middle-right pane.

Workspace window – This gives you a text workspace to work in. You can work with code, and assign variable values within it, and it will keep them persistent for you (the variables won’t disappear when you’re done executing a task), for as long as the window is kept on the desktop. You bring up a Workspace by clicking on the right tab on the Squeak desktop (called “Tools”). You should then find the Workspace icon, and click and drag it onto the Squeak desktop.

Transcript window – This is like an active log viewer. It’s basically output-only. You carry out your tasks in the Workspace window, but the output (for this test) goes to the Transcript window. You bring up a Transcript window by clicking on the “Tools” tab. You should then find the Transcript icon, and click and drag it onto the Squeak desktop.

DoIt (action) – This is how you interactively evaluate an expression in Squeak. It’s a universal command. Anywhere, in any window on the Squeak desktop, where you have legal Smalltalk code, you can select it with the mouse, just as you would with text in your native operating system, and hit Alt-D. This will evaluate the expression, carrying out the instruction(s) you selected with the mouse.

Running the Smalltalk Employee test

Run Squeak, and open the File list. Go to the middle-left pane and click on the small triangle to the left of the currently selected directory’s name (this is the Squeak directory). This will expand its directory tree. Select the Squeak sub-folder (this came from the zip file), and select “Employees-Test.st” in the middle-right pane. You will see a button appear in the top row of the window that says “filein”. Click on this button. This will load the Employees-Test package into Squeak.

Click on “Employee test data.text” in the middle-right pane of File list. Click on the lower pane, where the text of this file is displayed, then press Alt-A, and then press Alt-C. Open a Workspace window from the “Tools” tab, and then press Alt-V. (Note that many of the keyboard commands are the same as on Windows, but they use Alt rather than Ctrl).

You should also open a Transcript window (from the “Tools” tab).

Select the Workspace window. Select and evaluate every line of code in the Workspace. You can do this by pressing Alt-A, and then pressing Alt-D. This will “select all” of the text in the window, and evaluate it, which will initialize the collection of Employee objects, and run the test. Look at the Transcript window. That’s where the output will be.

The employee class symbol (in the last line of the Workspace) is set to #Executive. This will output all of the input data to the Transcript window. You can change the employee class symbol (to #Employee or #MidLevel) to get different results. All you need to do hereafter is select and evaluate the very last line of code in the Workspace to rerun the test.

Running the C# Employee test

Unzip the project files into a directory, load the project into Visual Studio.Net (2002 or 2003), build the project, and run it. The output will show up in a console window. The test code is initialized to use the Employee.zClass.EXECUTIVE enumeration. This will output all input data. You can change the employee class enumeration passed in to emp.PrintUsingViewerToken() on line 178 of main.cs (to Employee.zClass.EMPLOYEE or Employee.zClass.MIDLEVEL) to get different results.

Edit 5/16/07: I’ve made corrections to the C# version. See my post here.

Advertisements

9 thoughts on “C# (1.x) and Smalltalk case study #1

  1. One thing to point out, is that the huge code reductions come from being able to use techniques in dynamic languages that aren’t available in static languages. Things like dynamic proxies and decorators and #doesNotUnderstand let you solve problems in much more elegant and simple ways that just won’t work in a static language.

    Had your example been more complex and actually attempted to map those objects into a database, you’d find dynamic proxies drastically simplify lazy loading object references in from the database as they’re accessed. The C# approach simply couldn’t be written the same way or with as little code.

    Collections are also a big advantage the dynamic languages have, enumerating and iterating collections in C# require tons more code. 2.0 has anonymous delegates which actually allow a more Smalltalk style of programming, but only if you built your own collection hierarchy, the standard library sucks in comparison to Smalltalk’s.

    I know, because I’ve done all this, these are my two primary languages and I’ve spent much time making C# as close to Smalltalk as it can be allowing much more expressive code. Take one look at reflection in C# in comparison to Smalltalk and you’ll see another source of drastic code reduction.

    C#
    string name = anObject.GetType().GetProperty(“Name”).GetValue(anObject, null);

    Smalltalk
    name = anObject perform: #name

    If you use the same approach for both problems, there isn’t likely to be a big difference in code, only in productivity from the better environment Smalltalk has. Dynamic languages takes less code because they allow better approaches, not because of the less typing involved in not declaring types.

  2. Would you elaborate how to open this “code browser” window, that you are referring to in the article?

  3. @Ramon:

    Had your example been more complex and actually attempted to map those objects into a database, you’d find dynamic proxies drastically simplify lazy loading object references in from the database as they’re accessed.

    If you read my response to Hacknot’s post here you’ll see I agree with you. I was expecting to see an impressive difference in code size when I started on this first exercise, and I was surprised to see that wasn’t the case. So I speculated that I wouldn’t see it unless the problem was more complex.

    I tried out the idea of embedding the printing rules together with the data, or setting up the printing rules as a list, dynamically getting the instance variables and their values, and printing them out in a systemic way. In the end it resulted in no savings in lines of code. So I just stuck with the simpler, run-of-the-mill approach of outputting the data. In this case it didn’t pay to be clever.

    Re: collections require much more code in C#

    Perhaps you are talking about creating custom collections? As far as iterating over them, it’s been my experience that it requires one more line of code than in Smalltalk: a foreach loop construct.

    [edit 5/14/07 – A scenario I thought of that might fit in with what you’re talking about is if you’re filtering an existing collection to create a new collection. That’s one of the things I liked about Linq so much (and why I wished the final version was out 3 years ago). In Smalltalk it’s easy– collection select: [:each | –selection criteria on each–]. That’s it. Linq does things that are more advanced than this, but just having the ability to do this on a collection of objects would really help in .Net. TODAY would be nice!]

    Re: .Net 2.0 anonymous methods

    I was real curious about this: had Microsoft done anything to leverage the new anonymous methods feature in .Net 2.0. It sounds like they haven’t. Sad. I have the feeling they will do so in the next release of .Net. I’ve already seen the evidence in Linq. Microsoft should’ve come out with Linq in 2003, if you ask me. It’s sorely needed.

    Re: .Net reflection vs. Smalltalk’s

    I know. I dealt with .Net 1.x reflection recently. I couldn’t believe it. I said to myself, “It CAN’T be this much work! Come on!” But it was.

  4. @wxx:

    I wrote the article with the idea of giving people just enough information to run the Smalltalk Employees-Test package. Since you’re more curious I’ll explain.

    You can find the code browser by clicking on the “Tools” tab (on the right-hand side of the Squeak desktop). You should then look for “Browser”. Click and drag it to the Squeak desktop. It’ll show up instantaneously below your mouse cursor.

    The browser is made up of 5 panes. Across the top, the leftmost pane is for categories. Classes are grouped by categories. The next pane to the right is for classes. If you click on a category you’ll see its classes show up here. The next pane to the right is for method categories. Methods are grouped by these categories (there’s an “– all –” category as well, if you want to see all methods at once). Click on a class to see its list of method categories. The next pane to the right is the methods pane. The bottom, larger pane is for code. Click on any method to see its code here. Click on any class to see its definition here, as well. Object level variables are declared in the definition.

    To find The Employees-Test package, right-click in the categories pane of the code browser, select “Find class…”. Put “Employee” (without the quotes) in the textbox, and click on the “Accept(s)” button. It’ll take you right to it.

    Something to keep in mind is that there are two types of variables and methods: class and instance. Class methods and variables are analogous to static methods and variables in the popular languages of today. Instance methods and variables are analogous to, well, instance variables. You get the idea. You can see these different types by clicking on the “instance” or “class” button below the class pane, once a class has been selected. To avoid confusion, you should know that there is an actual object for the class definition, and the class instance definition. Despite my saying that class methods and variables are analogous to static methods and variables, the point where the analogy breaks is that the class definition is a real object, as is the class instance definition. The class instance definition defines the object you’ll get when you send the “new” message to the class object. It’s hard to explain. Hope this was enough.

    If you’d like to see the code browser in action, there’s a decent demo of it here. This will take you to Google Video. It’s a demo of the Smalltalk-80 system, which Squeak was based on, but there’s a lot done with the code browser. A lot of the information in there still applies to Squeak.

  5. @watt:

    Note my response to your post. I think your criticism of my naming technique, and some of my coding choices were valid, but I was not happy with your reading comprehension skills. 😛

    A fault that you pointed out, for which I am embarrassed, was the bug in the exception my code threw if DeptClassRules.ValidateDeptClass(dept, empClass) returned false. I ran into this bug I created earlier when I was debugging the rest of the program (I did the same thing in some of my other WriteLine() calls). You’re right. I didn’t test for the exception. I should have. I was getting mixed up between Smalltalk and .Net indexing. In Smalltalk all collection indexes start with 1 (like in VB 6). In .Net they start with 0 (like in C/C++).

    Your suggestion that I use the ToString() method in a few places is appreciated. It slipped my mind. My C#-fu is not what it used to be.

  6. Yep, .ToString() is implicitly invoked by the string printing routines, also that’s why you also don’t need to perform anything special to print out enums – System.Console.WriteLine("Department: " + dept); is enough. (I forgot to post that diff, I probably will still post it, since if you made that mistake, someone else is bound too.

    Thanks for the in-depth response to @wxx question, that’s excellent guidance!

    By the way, I will admit I was quite steamed about that error in catch() block – well, one may argue that confusing {1} and {0} is no big deal, but the issue is deeper than that.

    When you have failure in exception handling block (inside catch() block), you get catastrophich failure in production. First, your app crashes. Then you don’t know what the original exception was – since the exception that got thrown inside the catch() block actually overwrote the original cause exception information!

    Then (in production), you are truly screwed, since you need to 1) fix the exception handling 2) find out what failed originally. that’s 2 roundtrips until the issue can be fixed for good, and for that, your ass is grass.

    And the problem is, there is no solution. One may say that unit tests which verify all the error conditions are needed. Well, in reality, that’s just ain’t happening. Our apps have 2x more error handling code than actual code that does stuff, and while code that does stuff may work, we never know that about the error handling code. Because for good percentage of that, we will know only when it fails in that particular way.

    So, failing to be really, really careful while writing your exception blocks, is a grave sin. 🙂

  7. Tried to respond to the .NET 1.1 vs. 2.0 argument, but that just ended as a bitter rant against java type erasure (while also talking about type inference virtues in .NET 3.5 🙂

    http://watt.six.lv/2007/05/14/strawmen/

    The thing is, I couldn’t agree more on folly that is the untyped collections (Lists without type info) in .NET 1.1 or Java for that matter. Generics is the salvation.

  8. @watt:

    As I wrote my other, related post to this, “In defense of dynamic languages”, I thought about the relevance of primarily basing my argument on .Net 1.x’s weaknesses, since 2.0 has been out for 1-1/2 years (in the U.S.). I compromised, because I know from experience that in all likelihood there are still plenty of developers out there who are working with legacy 1.x code. All that code didn’t just disappear. 1.x, which was released in Feb., 2002 here in the States, was around for almost 4 years before 2.0 came out (released in Nov., 2005). Anyway, I did note in my post that generics in .Net 2.0 solve the “type erasure” problems I talked about (BTW, I love that term. I’ll be sure to use it in the future).

    I’ve heard about type erasure being an issue with Java generics, that even though they parameterized their containers (I guess), if you use reflection on the object you get out of a container it’s still of type “Object”, something like that. Has Sun fixed this by now? My guess is they would have, but I’ve been surprised before. I assumed they came out with anonymous methods about the same time Microsoft did, but apparently instead they have anonymous classes. All I have to say to that is, “Great. More cruft.”

    The reason the amount of code I have to create to do something is an issue for me is not because I’ll get sore wrists/fingers, but the amount of time it takes to code it. Many years ago I coded in assembly language. Physically I can deal with it. On projects, time is not something I have a lot of, in my experience. The project I did for this post was a simple example. Most projects I’ve worked on are in the thousands of LOC. When you get to that scale, the amount of code you have to write for each individual action or representation adds up. One of the main benefits of using a tool like Visual Studio is its code generation capabilities. If your problem domain stays within the constraints of the code it will generate for you, then you’re fine. As I noted in “In defense of dynamic languages”, the problem for me was I kept running into cases where VS could do nothing for me. It would get me to a certain point, and then it was like, “Sorry. You’re going to have to walk the rest of the way.” So, I put on my hiking boots… .Net could handle it, but VS was of no help. I just had to write the code myself. In fact, another problem I ran into sometimes is that I could create a more efficient overall solution by not using VS’s code generation capabilities, because its code always steered me to a one-size-fits-all implementation for solving a problem, which I could use, but it made the problem more complicated than it had to be. It’s always tempting for developers to take the easy route, let the tool generate the code for you, and then, no matter what the problem domain is, use the model it generated for you to try to solve your problem. They can end up paying for it later.

    As for your earlier criticisms of my C# code, I got inspired to fix some things on it. I’ll write a post about that later. I don’t think you’ll be entirely pleased with it, but I at least got the bugs out.

    As for your earlier comment, relating to the exception bug in my code, I see your point, but in this case I don’t see a reason to get so worked up about it. This code was for programmers. The main thing I was trying to convey with the code was the logic, what was required to accomplish a task, and the readability. Granted, I fell down some on the readability front due to the names I put on things. But I mean, this was a console application for crying out loud. You don’t see users running programs in DOS, do you?? I sure hope not. Had it been a web or GUI application, something that modeled user interaction, your criticism would’ve had more weight.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s