Intentional Software, my lovely and talented employer, put on a rather smashing demo at the DSL DevCon on Friday. You might like to watch the video, or perhaps read Martin Fowler’s thoughts.
Hey, Jonathan Edwards released the source and binaries for his Schematic Tables demo. (Paper, Video)
If you’ve got any belief at all that notation can make your life as a programmer better, it’s definitely worth checking out.
Blogger has decided that it doesn’t want to SFTP into HCoop anymore, so I’ve moved to WordPress. I’ve done my best to keep all of the URLs the same, including the Atom feed (which was an incredible pain in the ass to preserve; I couldn’t manage to make it work without directly hacking the source). You might’ve seen some hairiness on that end; it should be sorted now.
I’m hoping to update this a little more often now that I actually have the ability to do so again.
There is no better feeling on Earth than replacing a buggy, complicated, difficult to follow piece of code (which caused you untold pain six months ago as you spent weeks tweaking it into a superficially-working state) with an easy to follow solution, created in a day and a half, which is obviously and testably correct.
It’s like discovering that you’re one of those mythical superprogrammers, 10x more efficient than the “average programmer”, because the average programmer was you, six months ago. You might think that the comparison isn’t valid because I solved the problem once already, but the problem itself didn’t get any simpler, and my understanding of it only got significantly better once I sat down yesterday to understand what it was that I really needed to do. Even now I’d argue that the understanding of the problem that I have in my head isn’t really much better than it used to be; it’s the understanding I have on paper that’s clearly organized and checkable against my solution.
Solving problems isn’t about thinking really hard and hoping the solution that magically comes to you is close enough to being right that you can tweak it into something that works. It’s about organizing your understanding of the problem. When there are a finite number of cases, make sure you understand each one. I’d say more on this topic, but this is about all the blog post I have time for.
I have a friend who is taking a first-year computer science course. She’s come into this course without any programming knowledge. Her school requires her, for her degree, to take a number of introductory courses for various science disciplines — she isn’t going to take another computer science course after this one.
This introductory comp sci course teaches Java. Java, as you probably know, is a language in which the canonical “Hello, world!” program takes seven lines of code and requires you to invoke the following concepts:
- Classes
- Static methods
- Command-line arguments
- Visibility
None of which, you may notice, have anything to do with writing “Hello, world!” on the screen.
Now, this need not be an inherently insurmountable stumbling block — I wouldn’t go so far as to say that it is impossible to effectively teach programming in such an environment. But I did see one of her practice midterms. It tested her on the following concepts:
- Conversion rules from double to int
- Filling in the correct types for the methods in some class
- What happens when two variables point at the same object
- Writing code to perform some calculation
- Filling in methods that mutate some object
- What kinds of methods take “an implicit parameter” (which after some Googling, I determined meant the “this” pointer)
- Calling some methods on an object whose API was defined in two brief comments
There was no mention of the following concepts:
- If statements
- Loops
I talked to her again after she had taken her midterm. She’s learning about the if statement now. She’s also, at the same time, learning how to make UML class diagrams. She says that they talk in class about how they’re preparing her for work in big companies.
Is this not self-evidently ludicrous to anyone else? Does it ever occur to anyone that the way to
teach programming is not first to hope that they already know enough to get by, and then indoctrinate them into the cult of OO before they learn how to actually make the computer do anything? How is this better than learning Pascal? ARGH.
Sometimes I can’t help myself from trying to be the voice of reason in inane Slashdot threads. It’s a curse. Recently one came up entitled, “Staying On-Top of Programming Trends?“
“Trends are constantly changing, upgrading, or become popular due to high end user demand or just basic usefulness. I do my best to keep up with the trends, believing that for the most part they will be better then the current methods in place, or just comfort in knowing that if enough people use it, that there will be allot of help out there. Ultimately though, its keeping up with these trends and trying to figure out what’s a fad versus what’s actually useful that’s the difficult part. What do some of you do to keep up with the trends? Websites? Magazines such as Dr. Dobbs? Forums? I know there’s not one solve all, but for the sake of argument, suppose you wanted to stay on the forefront of Java based web development, what would you do?”
My response:
You want to know the latest trends for Java-based web development? Fewer and fewer people are going to be doing Java-based web development in the future.Fuck trends. They’re wrong. Every day the industry continues to stay with its current ridiculous technologies when vastly superior ones were invented decades ago infuriates me further. If it doesn’t infuriate you, you’re not paying close enough attention.
My advice: read Lambda the Ultimate and Steve Yegge’s blog. Endeavor to learn what the lambda calculus and referential transparency are. If you are sincerely interested in bettering yourself as a programmer and don’t go find out who Alonzo Church was then so help me God I will kick you in the balls. Learn about SML and type inference. Learn about Haskell and monads. Learn about process calculi and Erlang. Learn about Lisp and code generation and domain-specific languages. Learn about Scheme and lexical closures and continuations. Learn about Smalltalk and what OO was really supposed to be. Learn about type theory and formalism and the Curry-Howard correspondence. Learn about Forth and Joy and how you can have a powerful, expressive language without even so much as a grammar. Learn about Intercal and Befunge and just how badly your choice of programming language can torture you. Learn about UML and Ruby on Rails and Seaside and agile programming and Java generics and Python generators. Learn about aspect-oriented programming, context-oriented programming and concept programming. Learn about multi-paradigm languages like OCaml or Oz. Learn about weird Lisp dialects with syntax like Rebol or Dylan.
Realize that library design is language design. Realize that asynchronous programming with callbacks and explicit state in a world where lightweight coroutines were around in the days of fucking Simula in the 60s for Christ’s sake is cruel and unusual torture. (Sorry, pet programming construct.) Realize that the programming language research community, while considering systems programming a solved problem and generally not interested in talking about human factors, is doing some genuinely promising work. Did you know that there are concurrency models with strong type systems that can prove your system will never deadlock? Isn’t that fucking cool?
That ought to keep you busy for a little while.
By the by — I’m now working for Intentional Software. Since what we’re building is still pretty mysterious to the world, and my work now aligns pretty closely with my personal interests, I should mention that, obviously, the opinions and shit expressed on this blog are mine and do not represent those of my employer. There’s a seperate blog for that, anyway.
For the record: We are building some extremely cool things.
I’m always interested in the challenging of conventional wisdom. So I consider the 40-minute presentation I just watched on Immediate Mode GUIs to be time well spent.
Essentially: In a situation where your application is going to be constantly updating the screen — ie, a videogame — you can make some serious optimizations from the traditional object-oriented GUI paradigm which make your code a lot more clear. Rather than having event-driven binding, where you’ve got callbacks everywhere to update your model when the GUI state changes, and update your GUI when your model state changes, you draw the widget every frame, with parameters telling the library what should be in it, and get out of that single function call a response with which you can immediately act on.
Instead of Create-Update-Destroy, it’s one call to DoWidget, which may return an interesting value if the user’s done something interesting to it (clicked a button, etc). The logic is simplified immensely. For optimization purposes, you can still do caching on the library side (so you don’t have to rewrap the text in an edit box every frame, for example).
I’ve recently had some excellent results applying coroutines to GUIs (I’ve reached a point where I think hand-coded state machines are almost totally worthless if you have coroutines — unless you want to serialize or look into the state of a task); now I’m curious if I can combine the two ideas.
So, over the past little while, for a variety of reasons, I’ve been learning Forth. It’s a bizarre, wrongheaded little language that somehow manages to mostly turn its weaknesses into strengths. I can see how its inventor, Chuck Moore, was seduced by it, and by the philosophy of total simplification — once he simplified the problem of the compiler to ridiculous extremes, it fell apart in his hands and he was left with a rather powerful little language.
Some quick background:
Forth, as a language, is really, really simple. It’s so simple that there isn’t syntax, or even a parse tree. The Forth parser simply reads some sort of input until it comes across a space, looks up this “word” in its internal dictionary, and executes its definition. Argument passing is done implicitly, on the stack. You can think of Forth as a language where even data is code; “1″ is literally a word that means, “push ’1′ onto the stack.”
What this ends up meaning is that Forth is an aggressively postfix language. To say 1 + 2, you pass two arguments to + on the stack by writing 1 2 +. Now, Forth is flexible enough that you could rewrite + to read the next word from the input buffer and execute it first, or parse it as a number, or whatever, but it’s unForthlike and causes problems. For example, what if the word on the right side of + causes two items to be put onto the stack? It just doesn’t play nice with Forth’s model of the world, and addition is too fundamental an operation to not play nice.
What this suggests to me is that the most fundamental choice that every general-purpose programming language must make is how it will provide a way for abstractions to communicate.
Let’s back up a moment and make sure this is clear. The things that make code Forth-like are the things that make it play the nicest with other Forth code. On an informal level, this means taking advantage of the language features available to you when it is appropriate. These features are built to work smoothly with the language’s built-in abstractions; when everybody uses these abstractions, everybody works smoothly with everybody else.
Now, why is this interesting to me? Well, I firmly believe that the next important evolution in software development will only become possible when we have the ability to create domain-specific languages with a hell of a lot more ease than we currently can. We need not only to be able to pick and choose the abstractions that are relevant to the problem at hand, but to create new ones that play well with others. We need the ability to write code in the language of the problem. And rather than merely waiting patiently for XL, IP, or MPS to bear fruit, I like to think about what exactly the problem entails and how it could best be solved.
The biggest concern, in developing these kinds of systems, is — how can you develop a system to be completely flexible and extensible, when all of your components need to interact? You have to decide on some method for your abstractions to communicate, and you are building your entire system on the premise that you’ll never be able to predict all of the needs of your users. If you just pick one, as Forth does, you constrain yourself. You create problems that are unnecessarily difficult to solve, because you have to convert them into this protocol.
Some of you are probably screaming LISP! and LAMBDA CALCULUS! at me, like I was a great big moron for not having considered the mathematically pure options. Why, if your language gives you access to continuations and macros, there’s nothing you can’t build! Except… you know… embedded systems.
Sometimes, I need to be able to talk von Neumann. Sometimes, I need statically-calculated everything and no runtime. Sometimes, I cannot abide by garbage collection. And it kills me that I can’t do that and use real coroutines, which are safer, more efficient, and exhibit way better realtime behaviour than threads.
So I desire an environment that will allow me to drop into low-level abstractions when I need to. Not necessarily “inline assembler”, or “C code that interfaces with Lisp”, or whatever — I’ve written hard-realtime interrupt handlers in SML, just to see if it could be done. I just need the ability to use the appropriate level of abstraction, and I would really, really like to use the appropriate syntax at each level. Imagine an application consisting of a bunch of towers of abstraction, connected only at the bottom levels, and you’ll see the issue that I’m struggling with.
Built-in language constructs have traditionally had three advantages over user-defined abstractions:
- A custom syntax that is more comfortable to express problems in.
- Access to compile-time abstractions.
- Ubiquitousness.
#1 and #2 are fairly self-explanitory, and a lot of work is being poured into making them relatively solved problems (arbitrarily powerful macro systems that can flag domain-specific errors and do static code checking, etc).
#3, though, is important to think about further. An extensible development environment can take one of two approaches: It can provide a monolithic language core that all extensions build on, or it can attempt to build even the core elements of its language out of reusable, reconfigurable components. While the second approach is the more flexible, it comes with its own set of problems.
The big problem that I see is this — any user-written abstractions are probably going to build on some base-level abstractions. However, you don’t necessarily want to inherit an entire language just to use a single feature, and you want extensions to be able to work well with each other.
In essence, this is literally cross-platform development — building an abstraction that can sit on top of different kinds of abstraction towers, and building bridges between these towers when useful. This already happens routinely on the macro level; a hundred different language communities go and re-implement bindings for their own language for some useful C library. It would be kind of nice if this effort could be reduced or even eliminated, because it doesn’t SEEM like a fundamentally hard problem.
Consider the design difficulty, if you wanted to implement a portable abstraction for lazy evaluation. Do you assume you’ve got continuations? Or do you build it on less powerful abstractions? You could probably build it on top of something coroutine-like, but there’s just so many coroutine-like abstractions out there! Python’s got its generators, Ruby’s got its blocks, and some people won’t settle for anything less than Yield the Magnificent. They’re all sort of functionally similar, but there’s no standard conceptual map to build on. In the end, your nice feature that make things clean and concise ends up being intractably ugly to actually implement.
That can’t be what developers will actually end up doing; if they developed their whole project that way, for total reusability, it would be a collossal waste of effort and wouldn’t make anything even remotely easier. Developers will pick a set of abstraction towers that make sense for their problem, and build on top of them.
In the end, you can’t imagine an ecology of abstractions producing a single instance of an abstraction that will play nicely with all others. It can’t and won’t work that way. You will still see strong semantic platforms emerge, just like our current situation with lots and lots of languages incorporating lots of different ideas. Though abstractions are coupled to each other by nature, decoupling them from a standard language syntax makes it easier to build natural abstractions, without having to resort to writing your own compiler and designing a whole new language from scratch.
Essentially, just as there is no One True Abstraction for everyone to build on (no everything is not an object), there is no One True Abstraction through which other abstractions should communicate.
I just killed a couple of days reading Stevey’s Drunken Blog Rants, which is a fascinating account of a talented engineer spending a year searching for better languages, and finding Lisp in the end. Lots of interesting insights — I found his essays on practicing programming and math every day to be particularly inspiring, since I’ve already found Lisp, and Ruby, and OCaml, and Erlang, and pretty much all of the other languages that are mentioned. Except Rebol. That one was new.
I’ve resolved to read more of the CompSci books that, for no good reason, I haven’t gotten around to reading. I think I’ve somehow tricked myself into thinking that because Christophe de Dinechin hasn’t written a book about Concept Programming, that there are no books out there that have ideas worth thinking about. Obviously this is fallacious. Just because I might consider the idea that everything is an object to be — and I’m quoting here from Czarnecki and Eisenecker’s Generative Programming — “profane”, doesn’t mean I have any excuse for not having read Design Patterns. Hell, it’s not as if that’s stopped me from toying with Smalltalk. If you’ve got any great CS books to recommend, please do so in the comments!
I just rediscovered an excellent post about Forth and abstraction, sitting in draft form, from last June. I’m going to go clean that one up and post it.
This first-person shooter written in Haskell makes me happy. I feel I should learn more about functional reactive programming; it looks like a fantastically useful technique. I’ll definitely be reading that thesis.