raganwald
Tuesday, May 30, 2006
  Reg suffering from high-altitude hypoxia?
Respected Java and XML Guru Elliotte Rusty Harold has posted a very nice discussion of Eiffel's Design by Contract principles. Along the way, he makes a vicious ad hominem attack, accusing me of architecture astronautics (he actually stooped to using using the word "theorist," which means, someone who blogs when they should be coding). But I digress.

The World's Longest Single Arch Bridge

The gist of his post is that since we don't have language-level support for Design By Contract, we ought to use final to accomplish as much invariant enforcement as possible. He suggests that final should be the default choice, that interfaces should be rare and concrete classes plentiful in a design, and that support for polymorphism is less-important than enforcing invariants:
Preconditions, postconditions, and invariants are at the core of data encapsulation. They are the first and most important pillar of object oriented programming. Inheritance and polymorphism are second and third respectively, and are less important.
While controversial, this argument is cogent. I would say it is consistent with a specific kind of design philosophy, one that would be as at home in Ada or Modula as it is in Java. This is in marked opposition to the design philosophy of SmallTalk and Squeak, so I would say it is not very object oriented.

Is that a bad thing? I don't think so. Principles like data encapsulization, modularity, contract enforcement, and componentization are extremely valuable and can be realized in many different ways. For example, what do you think the Erlang programming language or the Linda communication and coördination frameworks are are all about? It's not just concurrency, dude.

The large-scale Java applications I've seen have been particularly devoid of true object orientation, and people seem perfectly happy with that. (Rough metric: the OO-ness of a Java application is congruent to the reciprocal of the number of public static methods in the source code).

So my conclusion is that if you want some advice on writing robust, large scale Java applications in a style that has been proven to be useful over the last several decades, you will find Rusty's post informative.

p.s. My tongue was in my cheek when I composed the title and suggested that one of the most socially conscious Java bloggers in the world was vicious. But you knew that.

p.p.s. If you feel that either Rusty or I can be correct but not both of us, please accept my condolences. How long has it been since imagination passed out of your life?

Labels:

 

Monday, May 29, 2006
  In memoriam (Repost)
There's nothing I can write that could possibly capture how incredibly lucky I am to be able to live, love, and follow my dreams in the New World.

A generation of our youth poured over the trenches and into the gas in WWI, and then their sons and daughters went back to fight and die and ultimately prevail in WWII so that you and I could live in a world where people of diverse colours, sexual orientations, religions, and political viewpoints could live without fear of extermination and genocide.

Thank you.

(Originally posted on Remembrance Day).
 

Thursday, May 25, 2006
  Ready, Aim, Final
There're some interesting rants going on about Java's final keyword. And Mike Bowler does a decent job of explaining what final does. As Mike points out, all of the ranting is about whether you should declare classes and methods final.

The arguments against declaring classes and methods final are persuasive. It makes it really hard to unit test anything. You are putting future programmers in a straightjacket (that's actually part of the spirit of bondage and discipline languages like Java, but that doesn't mean it's a good idea). And some refactoring becomes impossible.

When I look at these arguments, I see evidence of a deeper malaise. The problem isn't the final keyword. The problem is the lack of separation between interfaces and implementations.

Take java.lang.String. This class is final, and that is a royal PITA. If you want to extend String to do neat things like keep track of SQL injection safety you are SOL. Now if there was a String interface, and everyone declared their instance variables with the interface instead of the implementation, there would be no problem with a final String class.

Do you need a flag for whether the String is SQL-safe? Use delegation instead of inheritence. No problem. Do you need pluggable storage behaviours for handling special character sets? Use aggregation and a Strategy pattern.

Separating the String interface from the String implementation solves our problems nicely.

I'm 100% ok with the final keyword in implementations. I think it's ok to make a class and say, this is the FizbangImplementationClass. For example, if you were making a sort class for some reason, it would be a little weird to override the actual sort algorithm. If you do, maybe it isn't really a-kind-of FizbangImplementationClass, so it shouldn't extend that base class. It ought to conform to the same interface but be implemented as something else.

At no time in the near future will I be proposing JSR666, "How to fix the Java language so that you can write elegant, object-oriented code." But if I do, my suggestion will be to make every class an interface automatically. (This is not an original idea.)

Here's how it would work. We keep all of the existing syntax exactly as we have it. But the first addition is that you can have class A implement class B. Note that class B is a class, not an interface. This means class A acquires the public members of class B but not their implementations. Now you can 'override' to your heart's content. The final keywords do not affect this, because you are actually implementing an interface.

Is this bad? I don't think so. Framework authors that declare methods final are usually trying to ensure some consistency within a class. If you are implementing the class instead of extending it, you can build it from the ground up. If you need some of the class's behaviour you can use delegation and the orginal class will retain its original behaviour.

The second addition is that when you declare a variable to be of a certain class, you are naturally declaring it to be the interface of the class. So anything declared as a String is actually declared as the String interface, not the String class.

(There are other, subtle changes that are needed. Should String.class refer to the class and String.interface refer to the interface separately? Order plenty of espresso and let the committee thrash this kind of question out).

This could be all accomplished within the current language if everyone was disciplined about declaring interfaces. However, Gosling and Steele set the tone for the Java language when the original libraries contained precious few interfaces and tons of classes. I think it's too late to get everyone to change. So some language change is needed to handle the zillions of lines of existing code.

Or, you could take another approach and eschew languages that deliberately restrict your expressiveness. It's a big world out here, and it would be a shame if you don't get out from behind your desk and travel once in a while :-)

Updates: Elliote Rusty Harold triple-underscored his support for the final keyword, while suggesting that interfaces are mostly bad, and I responded with respect for our differences.

Then Rusty provided some excellent reasons for locking interfaces down in his designs, and I explained why I believe there're legitimate reasons to choose Rusty's approach for some programmers and more flexibility for others.

Labels: ,

 

Wednesday, May 24, 2006
  Beam me up, Scotty

Beam me up, Scotty, originally uploaded by raganwald.

 

Tuesday, May 16, 2006
  Development is about communication between people

 

Monday, May 15, 2006
  JavaScript Pop Quiz Remarks
Here's an email I sent in response to a colleague's question about some sample JavaScript. If you haven't already seen it, you might want to look at the code first.

Joe:

You seem to understand the behaviour perfectly. The problem was that we wanted to call set_datetime(...) with a different value for each cell in the table. We know the values when we build the table cell by cell.

However, if we write the code as:

    thisCell.onclick =
function() { set_datetime( dt_current_day.valueOf(), true);

At run time each cell will have a function that gets the value of dt_current_day when the cell is clicked, not when the code was written into the cell. I think we end up with the last day in the calendar. We want to capture the value when of dt_current_day when we build the function, not later when the function is called.

The code given makes an anonymous function (let's call it the "outer function") and calls it immediately. That call copies dt_current_day.valueOf() into the day variable and returns a function (the "inner function") that uses the day variable. The inner function is the one assigned to the cell's onclick handler.

The reason it works is that every time you call the outer function you manufacture a new day variable. If you did the assignment without calling a function, like this:

    var day = dt_current_day.valueOf();
thisCell.onclick =
function() { set_datetime( day, true);

...you would end up with the wrong behaviour because all of the onclick handlers would share the same day variable.

This pattern is so common that Lisp-like languages (including Scheme) have a family of macros called let to do this for us. In Scheme, our code would look something like this:

    (let (
(day (value-of dt_current_day)))
(!set (handler thisCell 'onclick)
(lambda () (set-datetime! day #t))))

Although using let looks just like a straightforward assignment using set!, it actually creates a new variable every time. It should not surprise you to learn (if you didn't already know) that behind the scenes let is a macro that rewrites the code into an anonymous outer function just like the JavaScript sample.

p.s. This problem can be solved in other ways. The original code actually wrote the onclick handler out as an href in plain text. It copied the value of dt_current_date out as a string. Essentially, it was doing "code generation." Also, it would be possible to create an object with a field to store the date. You'd have to do that in a language like Java where functions don't really exist independently of objects and classes.

p.p.s. I can't believe I managed to explain the whole thing without resorting to the c-word!
 

Tuesday, May 09, 2006
  JavaScript Pop Quiz
In essay or point form, compare and contrast:

    thisCell.onclick = function() { set_datetime( dt_current_day.valueOf(), true); };

and
    thisCell.onclick =
function (day) {
return function() { set_datetime( day, true); };
}(dt_current_day.valueOf());

Do not limit yourself to describing the run-time behaviour as specified in the language specification. Discuss the implications of each choice in the broader context of programming and software design.

Hint 1: Consider the possibility that these statements may be executed more than once, that this_cell may refer to different objects when these statements are executed more than once, and that dt_current_day may have different values when these statements are executed more than once.

Hint 2: SICP.

Hint 3:

Hint 4: JavaScript Pop Quiz Remarks
 

Tuesday, May 02, 2006
  How to make programming hard for yourself
I posed a simple question: why do we have trouble admitting that programming is hard?

This generated a tempest in a teapot of comments. Most focused on the red herring of whether programming is an innate talent. That's actually irrelevant to the fundamental question.

Is programming actually hard?

The evidence is in that software development as an engineering practice is very hard. One person on reddit commented that he had worked for three successive game companies that shipped working software on time. Other than that comment, my own experience, the experience of everyone I've ever talked to, and the industry folklore is that software projects are almost always late, short of promised functionality, buggy, or all three.

Peek-a-boo!

Now I admit that lots of people think that this is a management problem. Their view is that the actual programming is quite tractable, it's just that getting people to work on the right things at the right time is the hard part of software development. This argument takes the same world view that the Egyptian engineers had about building pyramids: the labour is so easy that ignorant slaves can do it.

My view is that this is true for a certain class of programs. Most of these can be found in current business contexts. Quite honestly, most business applications are what I would call wide and shallow. There's a lot of work to be done, but little of it requires deep thinking. The challenges are around managing requirements and user expectations. These are hard problems, but they are not in the same class as, say, figuring out how to implement a fast join on two distributed hash tables.

I think that easy programming problems drive a lot of decisions about people and tools. If you perceive that a problem isn't very hard, why hire the 'cream' of the programming crop to solve it? And if you eschew the cream for a plain glass of 1% milk, won't you insist that the program be written in Blub instead of Python, Ruby, or Lisp?

This becomes a feedback loop. The 'B' players become hiring managers one day and they hire 'C' players. The 'C' players are asked to design applications, and they design things that they know how to build. This second effect is a huge lever pushing programming problems downwards in business.

Programs are written to solve problems posed by people, be they programmers, program managers, business analysts, or what-have-you. The difficulty of writing the programs is driven by the difficulty of the problems. The difficulty of the problems is limited by the imagination of the people posing the problems.

If your feedback loop is pushing difficulty downwards, the only problems you will have are the ones that can be solved by anyone who can pick up a Learn Blub in Twenty-One Days book.

Is this a problem?

Quite honestly, no. If what you want is a steady paycheque, and you can sleep at night knowing that your job might be outsourced one day, there's nothing wrong with chasing the easy work. It's no different than playing saxophone in a dance band at a wedding. Charlie Parker did it, you can do it.

But just because almost everybody does easy work doesn't mean there isn't hard work to be done. Hard problems are their own reward for some people, myself included (in my case I mean "hard for me," I know more than a few people that consider my hard problems to be recreational diversions). And some hard problems have lucrative consequences if you have a taste for starting companies.

Why it's hard to find hard problems

It's hard to find problems that are so challenging that you don't know how to solve them. It's especially hard if you also want a problem that can actually be solved. And it's very, very hard to find a hard problem that makes business sense, even in a startup. Most good startups solved the right problem rather than the hardest problem.

Very few people have the luxury of writing requirements for a program that they have no idea how to write, much less the imagination to see the results. Jump in your time machine and zoom back to 1980. Start asking people do sketch a word processor design on a napkin. How many people specify bit-mapped graphics and mouse-driven selection?

I compared programming to music and athletics. I suggested that programming is hard and the reason we don't see that is because we aren't competitive enough. That may be true, but now I sense a deeper challenge: in programming, we tend to pose questions for which we already know the answer.

In music, most people's reach far exceeds their grasp. It's quite easy to find pieces of music that are difficult or impossible for someone to play but nevertheless pleasing to that same person's ear.

If you are a wanna-be bassist and you listen to Jaco Pastorius' harmonics on "Portrait of Tracy," it is immediately obvious to you that you could be better and that being better is worthwhile. I leave it to your imagination to draw the same parallel to sports and athletics.

But in programming... we have a nasty habit of being very satisfied with the tools we already know. After all, they are good enough for all of the programs we have ever written. And, surprise surprise, they just might be good enough for all of the programs we will ever want to write.

How to make life hard for yourself

Most of us need outside forces to shake us up. Many bassists were shocked in 1976 when they heard Portrait of Tracy. Harmonics were used as accents here and there, but nobody dared to play an entire piece with harmonics as the main technique. This shook things up, and now playing harmonics is considered a basic skill on electric bass (although it's still fiendishly hard to play Portrait of Tracy!)

For all of my professed admiration of Ruby on rails, I personally don't think that easier and more productive CRUD application writing will shake things up. I personally care very much about writing applications in a tenth of the time, but using Rails is like listening to Jaco Pastorius. The real learning experience comes when you try to duplicate the feat.

If you want Rails to help you improve as a programmer, you need to look at the source code and figure out how to use Ruby to write Domain Specific Languages. Next, you'll have to write a few DSLs to become immersed in how programming changes when you can write DSLs. Now you're ready to look for a problem that is barely possible if you can solve it with a DSL.

Before you knew how to write your own languages, you probably couldn't design a program that couldn't be written without a DSL. Now you can.

This general template works surprisingly well: Learning things that force you to solve existing problems in new ways will help you find hard problems that can only be solved with the aid of your new techniques.

So when you're done learning how to write DSLs just like Rails, go ahead and write a web application using continuations as the basic metaphor. And don't forget to look under the hood and figure out how the framework makes continuations scale in a web context.

Come to think of it, figure out how to write continuation-based Rails applications that live on a Google-scaled grid and you might be headed towards a place where the inhabitants have no fear of hard.

I'll see you there.

Update: I found an early take of Jaco playing Portrait of Tracy (MP3). Nice.

Labels:

 

Reg Braithwaite


Nota Bene
A Brief History of Dangerous Ideas

Share
rewrite.rubyforge.org / ick.rubyforge.org / andand.rubyforge.org / unfold.rb / string_to_proc.rb / dsl_and_let.rb / comprehension.rb / lazy_lists.rb

Beauty
IS-STRICTLY-EQUIVALENT-TO-A / Spaghetti-Western Coding / Golf is a good program spoiled / Programming conventions as signals / Not all functions should be object methods

The Not So Big Software Design / Writing programs for people to read / Why Why Functional Programming Matters Matters / But Y would I want to do a thing like this?

Work
The single most important thing you must do to improve your programming career / The Naïve Approach to Hiring People / No Disrespect / Take control of your interview / Three tips for getting a job through a recruiter / My favourite interview question

Buy Raganwald a Coffee
If you enjoy reading my weblog, please consider buying me a Darkhorse Double Espresso, for just $3.15 Thank you!

Management
Exception Handling in Software Development / What if powerful languages and idioms only work for small teams? / Bricks / Which theory fits the evidence? / Still failing, still learning / What I’ve learned from failure

Notation
The unary ampersand in Ruby / (1..100).inject(&:+) / The challenge of teaching yourself a programming language / The significance of the meta-circular interpreter / Block-Structured Javascript / Haskell, Ruby and Infinity / Closures and Higher-Order Functions

Opinion
Why Apple is more expensive than Amazon / Why we are the biggest obstacles to our own growth / Is software the documentation of business process mistakes? / We have lost control of the apparatus / What I’ve Learned From Sales I, II, III

Whimsey
The Narcissism of Small Code Differences / Billy Martin’s Technique for Managing his Manager / Three stories about The Tao / Programming Language Stories / Why You Need a Degree to Work For BigCo

History
06/04 / 07/04 / 08/04 / 09/04 / 10/04 / 11/04 / 12/04 / 01/05 / 02/05 / 03/05 / 04/05 / 06/05 / 07/05 / 08/05 / 09/05 / 10/05 / 11/05 / 01/06 / 02/06 / 03/06 / 04/06 / 05/06 / 06/06 / 07/06 / 08/06 / 09/06 / 10/06 / 11/06 / 12/06 / 01/07 / 02/07 / 03/07 / 04/07 / 05/07 / 06/07 / 07/07 / 08/07 / 09/07 / 10/07 / 11/07 / 12/07 / 01/08 / 02/08 / 03/08 / 04/08 / 05/08 / 06/08 / 07/08 /