Monday, June 3, 2013

Elegance with a balance

In my introduction to Project Effes, I talked a bit about why I want to create a new programming language (tl;dr: because I think it'd be fun). I didn't talk much about what I'd want that language to look like, though. Time to start building.

So to start, let me say that I like languages with an ethos. Haskell is a great example of such a language; it takes just a few core concepts and runs with them in a very elegant way. Where the concepts are different, they're often parallel; you can curry functions, but you can also curry generic types. Pretty cool!

That makes for a beautiful language, but can also make it hard to use. As powerful as the abstractions are, sometimes you want a cigar to just be a cigar. To that end, Haskell does make some concessions in the name of usability. Its do notation makes functional programming seem imperative-ish, and its record syntax is a bit of a hack.

Ruby, on the other hand, feels like it's almost all hack. Lambdas, procs and blocks are kindasorta similar, but they don't quite fit together; it feels like the language didn't know which direction it wanted to go in, so it chose to go in all of them and didn't care that it got pulled apart. The decision to make everything an expression seems principled in a Haskell-type way, except that a lot of those expressions are actually pretty meaningless; a for loop isn't something you'd typically expect to have a value, and it's not clear to me what that value should be, anyway. Other than the ability to say "neat, everything is an expression," what does that actually buy you?

Then there are languages that draw lines in the sand and immediately tip-toe over them. Scala seems to do this by declaring that static functions are Wrong and Can't Be Done In Scala, but then inventing companion objects that let you define singletons with instance methods. Am I crazy, or does that sound very static-function-esq? (Full disclosure: I haven't actually used Scala yet, and I'm sure there are subtle differences between companion objects and static functions; I don't know if they're enough to justify crossing over the philosophical line in the sand. It could also be that I've misinterpreted how strong of a line that is.)

I think it comes down to picking a few abstractions — resisting the temptation to force everything into a single, base concept — and ensuring that they're cohesive and complementary. Features that are similar should be similarly constructed; those that are fundamentally different should be treated as such; and if you have a set of features that are so similar that they're basically a bikeshed problem, just pick a color and go with it. There's a balance to be struck between a Haskell-like level of abstraction and the simplicity of adding a couple more base concepts. Easier said than done.

What I think this means for Effes is a blend of functional and imperative programming styles. I want each one of those components to work well on its own, and I hope they work well together, but I don't want to artificially define either one in terms of the other. A fold and a for loop are different. They feel different, they act different, and they make you think differently. I want each to be a well-formed, first-order construct. Going back to the Haskell case of do notation, rather than having sugar that makes a functional idea look kind of imperative, I'll create a context where the design is imperative, with all of the bells and whistles you'd expect.

No comments:

Post a Comment