Saturday, September 11, 2010

The problem-solving process in dynamic vs. static languages

This post is about why not to used static languages. It will outline the creative process used when starting to attack a problem and implementing its solution in code, and why that very process is hindered greatly by static languages.

You know what I mean, when I say static languages (Java, Scala, C++, et.c.).

I know that I've read about similar ponderings to these on the web earlier but a) I can't find them right now and b) It struck me anew on a personal level when I tried to understand why I work so much quicker when developing code in a dynamic language (especially JavaScript).

The dynamic development process

When I start to formulate a solution to a problem in JavaScript, I focus on the algorithm, on the classes that operate on the data and their methods - on the flow of function calls between different parts of the system I build.

Most often I forget some details about the data being passed or of what kind of state I need to include in the arguments in calls between different classes. This is no big deal as I just create new objects on the fly as arguments or return values, sometimes currying them up as part of a filtering process.

This lets me stay 'in the zone' and focus on the problem I'm trying to solve. The data structures are pliable and can be changed in the same place as the code. I never need to bothered with what kind of data I'm juggling. I might do something like this;

foo.barMethod({size: x, noses: 17, frotz: {a:'b', c:'d'}}, this.somethingImportantAtTheMoment);

The first barMethod argument is an object. Just an object, could be anything. When I need to put more stuff in there, I do so. The object is so lightweight that it doesn't warrant its own class, it is really just a simple wrapper around some data I'm passing, pliable as my code for using it are right now in my problem-solving process.

The static development process

This might differ significantly in Scala which I must confess I haven't studied enough. Please let me know in the comments if so. But when working with Java, I very quickly run into the creation of (often hierarchical) the definition of data classes and interfaces.

Their properties are often complex, themselves objects, a couple of layers deep. Even though each data class is trivial, creating them takes me out of my creative zone, forcing me to make decisions around data that I'm not ready for yet. So naturally I put in the bare minimum of information.

Then when using and passing around these objects, I often find myself constricted by the fairly arbitrary member/property types I've chosen, whose management tend to bleed out into the code, in the form of casts, et.c (This should be an area where Scala does help, though).

When I change my strategies for my problem-solving logic, it impacts heavily on the contents and types of the data classes, even if the data and logic classes are the same. I need continually to pop out of problem-solving mode into yak-shaving mode to arrange the data to fit my new logic.

I have now realized why static people so often tout the importance of having refactoring zombie-dust-powers in their extensive IDEs. From my vantage point it seems like fixing a language deficiency.

But wait!  What about all that evil terror that happens if you just create anonymous objects and functions mid-stride without any type enforcement?  Well, for one thing - testing is key, and another thing - it has actually happened once or twice that I've had to hunt about in somebody else's code for what on earth they have put in an argument object.

Compared to the extreme pain of always having a static retriever biting my private parts, the problem of finding the dynamic bottle of beer in the living rooms has in my experience been an extremely fair trade-off.

YMMV, of course.



Micke said...

Can't say say more than: agreed!

Alex Kotov said...

Perl is a dynamic language.

By having combination of scalars, hashs and function references, most of programs don't need in most of classes that such programs would have to have in Java, for example )) It significantly speeds up the process!

On the contrary, the power of the static languages is the static control of types and function arguments. Unfortunately, most of dynamic languages are not so safe to write in this point...

Zoom said...

I do not agree that this is about static or dynamic typing, I think it is about different styles of analysis.
As I see it programming is all about abstraction. You must abstract away details of a particular problem and create larger pieces of the puzzle to be able to finish the entire picture.

Your example deals with abstracting away details in data-containers for example to use as a parameter to a function. I will suggest that this is a natural and efficient way when dealing with a class of problems I like to call "structured programming". It is particularly suitable for dealing with web-stuff that is a basic request/response scenario.
The stereotype solution there is take data A from a HTTP-POST, check some simple stuff and convert to format A', pass it on to some more advanced logic that refines the data into A'' and make it persistent.
In this scenario it helps to be able to "forget" details about A when creating the methods that refines the data. You start out with a basic idea about what A should contain and as you progress you might change details in all layers and no static type systems interrupts your flow.

In contrast we stiff-lipped and static types like to attack the problem by designing APIs. For example my latest Android hack contains an API for getting an Atom feed and extracting some pieces of data. The problem I wanted to abstract away was not the data complexity. I had more problem with thread safety and concurrency. In order to abstract that away so that I could forget it I had to mandate what methods classes had to have.
The static typing here really helps, when I inherit from the generic class handling threads I know what to implement since the type systems tells me. I know I "do the right" thing and that there will not be any concurrency issues in runtime.

By these two confused examples I try to point to differences in designing applications and designing APIs. I also think that some people like the API-analysis better than the data-abstraction-analysis method and that different classes of problems lends themselves better to one or the other.

Peter Svensson said...

@Zoom. I think that the problem I described is indeed generic. You describe another kind of problem, managing threads, which I would describe as perpendicular to the issue about static vs. dynamic typing as such.

In fact, very little of the code I write daily have anything to do with sending or retrieving data from the server.

It could be converting a set of tables into a tree structure, or delegating formatting definitions for a grid/table or any number of things. I am after all writing actual applications - just not in statically typed languages (and in the browser).

I also have the luxury of not ever having to see a thread - only asynchronous callbacks.

But anyway, my point is that not having to worry about types is a generic benefit, completely unrelated to any specific area. I suppose you wouldn't suggest that types are only beneficial when dealing with Threads?

Neither am I suggesting that the absence is. QUED. so here, whatnot :)

Mystilleef said...

Well said. That's why I don't use statically typed languages to solve problems, if I do at all.

I use Python for problem solving and experimentation. Then if, and very rarely, needed I painfully convert the solution into a statically typed language.

But these days I use Python for everything. And I think it's crazy that people use statically type languages for anything. It's a huge creative drawback.

Of course for writing libraries, drivers, compilers, and low level infrastructures use C. Then provide wrappers for higher level languages.

primavera133 said...

"I have now realized why static people so often tout the importance of having refactoring zombie-dust-powers in their extensive IDEs. From my vantage point it seems like fixing a language deficiency."

So true, so true...

Mats Henricson said...

The text is a bit too vague for me to really understand what you are talking about, but from my understanding Scala's dynamic inheritance, together with type inference and case classes sounds like a solution for you.

Zoom said...

No, what I am saying is that the problem solving process you describe is not that generic, it is suitable to a certain class of problems. Other people solve other classes of problems and have other approaches to reducing complexity.
API design is an example where I think that static typing actually helps. Type systems can be seen as a language that express the assumptions that I, as the API designer, hava about the caller of the API.
In what way can it be a problem that I _tell_ you what my assumptions are?

Peter Svensson said...

@Zoom I agree that I was too specific. And in principle I find nothing wrong with your argument if you know exactly how to implement the solution to your problem (API design or whatnot).

What I am describing is the averse effect of statically typed languages in the creative process of trying to find a solution to a problem by some false starts, rewrites, going backs et.c.

When you have static typing all decisions are 'sticky' and makes 'thinking in code' more like stamping through a swamp than walking on normally.

Which lead directly to dependency on IDE plugins for code generation and refactoring which works mostly well, if you have the right JAR-file versions, et.c.

Duck-typed languages are more nimble to work with, regardless of the kind of system you are trying to make, unless, as I said, in the specific case where you know *exactly* how to implement something you will never need to rewrite or refactor, and then, sure, it's better to see the interface in code than in comments.

Zoom said...

It seems that we really can't get a flame war started since we actually agree. Oh well, I tried :-)