Following is an excerpt from Masterminds of Programming, by Federico Biancuzzi and Shane Warden. (adapted for the web).
When Microsoft settled a lawsuit from Sun Microsystems over changes to the Java programming language, they turned to veteran language designer Anders Hejlsberg to design a new object-oriented language backed by a powerful virtual machine. The result was C#--and a replacement for both Visual C++ and Visual Basic within the Microsoft ecosystem. Although comparisons to Java are still inevitable in syntax, implementation, and semantics, the language itself has evolved past its roots, absorbing features from functional languages such as Haskell and ML.
The Future of Computer Science
What do you consider the outstanding problems in computer science?
Anders: If you look even at a metalevel above that, the ongoing trend in language evolution has always been this constant increase of abstraction level. If you go all the way back to plugboards and machine code and then symbolic assemblers and then C and then C++ and now managed execution environments and so forth, each step we moved a level of abstraction up. The key challenge always is to look for the next level of abstraction.
There are several contenders right now that appear to be big challenges. One we already talked about is concurrency: producing meaningful programming models for concurrency that are understandable by the broad masses of programmers as opposed to a few high priests of parallelism. That's kind of the world we're in right now. Even the high priests at times get surprised by their own code today. We have a big challenge there.
Also there's a lot of talk about domain-specific languages and metaprogramming in the community these days. In my opinion, there's more talk than reality there. I don't think we know what the answers are. You see things like aspect-oriented programming and intentional programming, but we have yet to really nail it.
Depending on who you ask, people go either, "Oh, there are no domain-specific languages," or "Oh, domain-specific languages are everywhere." We can't even agree on what a domain-specific language is, in a sense--but there's clearly a there there when it comes to devising more declarative ways of expressing yourself. In some ways, we've run out the line all the way on imperative styles of programming. We're not going to get much further with our imperative programming languages. It's not like there are new statements that we could add that would make you 10 times more productive all of the sudden.
I think one of the things that are true of most programming languages today is that they force you to overspecify the solution to your problem. You're down there writing nested for loops and if statements and whatever, and really all you wanted to do was a join between two pieces of data. But there's nothing that allows you to say that. You have to get down and dirty and do hash tables and dictionaries, blah, blah, blah.
The question is how do we move to that more declarative level of programming. Of course, the more you move it up, the more concepts you end up having because you get more domain-specific. There's a lot of truism to this dream of domain-specific languages, yet we somehow have not found the right vehicle to implement them, I feel like. Yet. So that does remain a challenge.
Right now, we're seeing this interesting resurgence of dynamic programming languages. I actually feel it is really in spite of the languages being dynamic and more because they have great metaprogramming capabilities. Like if you look at Ruby on Rails, it's all powered by Ruby's metaprogramming capabilities, not so much the fact that it's dynamic. It just so happens that eval and metaprogramming are a lot easier in a dynamic language than in a static language.
On the other hand, it's a high price to pay to give up your statement completion and your compile-time error checking and so forth.
Depending on who you ask, people go either, "Oh, there are no domain-specific languages," or "Oh, domain-specific languages are everywhere." We can't even agree on what a domain-specific language is, in a sense--but there's clearly a there there when it comes to devising more declarative ways of expressing yourself. --Anders Hejlsberg
The argument I've seen from lots of people who've been around dynamic languages for a while is Smalltalk's browser.
Anders: I'm not sure I buy that. That works when your problem is small enough, and it used to be that problems were small enough back when Smalltalk first appeared. Now with the size of the frameworks, it is unfathomable to think that people actually know all of the APIs that exist on a particular object or even care to know. Tools like statement completion and Intellisense and refactoring driven by compile-time metadata or static typing are just invaluable. They're going to continue to be more valuable because the world is going to keep getting more complex. Right now we're seeing a surge of dynamic programming languages, but I think in large part it is powered 1) by the metaprogramming angle, and 2) it's in many ways just a reaction to the complexity of the J2EE environment.
I've certainly seen lots of Java programmers defect to Ruby just because they're dying in frameworks and Struts and Spring and Hibernate and what have you. Unless you are a grand wizard of technology, you're not going to be able to put all of these things together yourself.
Should programming be more accessible to people who aren't and have no aspiration ever to be grand wizards?
Anders: I think so. It all depends on what you mean by programming also. Because in a sense, is using a spreadsheet programming? If you can make people program without them even realizing they're programming, then, oh my God, that's wonderful. I harbor no aspirations that we've got to teach the world users how to write programs in the kinds of programming environments that we use as developers today. Certainly, programming, yes, but at a much higher level.
What's facing us now and in five years?
Anders: Concurrency is the big one right now. That thing is right in our face, and we've got to find solutions to that problem. One of my biggest challenges in the foreseeable future is having our team work that issue.
Again, we'd like to do it in an evolutionary fashion, but how do you deal with the shared state problem and side effects without breaking all the existing code? We don't know yet, but it very well may be that that concurrency is a big enough paradigm change that whole new languages are needed or whole new frameworks are needed. Although I don't think we're at that point yet.
I think there's a lot of ground that we can gain from making it possible for people to write APIs that internally are massively parallel and written by people that really understand a particular domain, be it transformations or numeric processing or signal processing or bitmaps or image manipulation. And yet, put APIs on it that look largely synchronous from the outside and in a sense, wall off the concurrency to inside the APIs.
There are things that are required in our programming languages today in order for us to be able to do that properly. One of them we already have, which is the ability to pass code as parameters. As APIs get more and more complex in their capabilities, you can't just pass in flat values or data structures to the API. You've got to pass in pieces of code that the API then orchestrates and executes.
You need higher-order functions and abstractions such as map, fold, and reduce.
Anders: Higher-order functions. Exactly. In order to be able to do that, you need stuff like lambdas and closures and so on. In order for you to be able to do that in a concurrent environment, you also need guarantees on whether these lambdas are pure, or do they have side effects. Could I just automatically run this in parallel, or are there side effects that would cause that not to be possible. How can I know that? Those are things that we don't have in our languages today, but we can certainly speculate about adding these. Of course, the trick is adding them in a way that doesn't constrain you too much and that doesn't break too much of your existing code. That's a big challenge.
That is something our team thinks about daily.
Does the need for concurrency change the implementation or also the design of the language?
Anders: Oh, it certainly changes the design. A lot of people have harbored hope that one could just have a slash parallel switch on the compiler and you would just say, "Compile it for parallel" and then it would run faster and automatically be parallel. That's just never going to happen. People have tried and it really doesn't work with the kind of imperative programming styles that we do in mainstream languages like C++ and C# and Java. Those languages are very hard to parallelize automatically because people rely heavily on side effects in their programs.
You need to do several things. You need to first of all construct modern APIs for concurrency that are at a higher level than threads and locks and monitors and where we're at now.
Then there are certain things you need from the language to make that style of programming easier and safer, like guaranteed immutability of objects, pure functions where you know there are no side effects, analysis around isolation of object graphs so you know whether a particular reference to an object graph has ever been shared with anybody else, and if it hasn't, then you can safely mutate it, but if it has then there might be side effects. Things like that; things of that nature where the compiler can do some analysis and help provide safeties, like we have type safety today and memory safety and so forth.
Those are some of the things that I think need to happen in the next 5 or 10 years in order for us to better be able to program in these concurrent systems.
Essentially you are telling the computer what to do.
Anders: That's one of the big problems with the very imperative style of programming that we do now is that it is indeed very overspecified. And that's why it's hard to automatically parallelize.
In the future, might we let the framework do the work to deal with concurrency?
Anders: Oh, I think so. There are many different kinds of concurrency, but if you're talking about data-parallel kinds of concurrency where you're going to do operations on large datasets like image manipulation or voice recognition or numerical processing, then I think it's very likely or very appropriate for us to have a model where you just view it as an API. You have a higher-level API where you can say to the API, "Here's the data and here are the operations I want to have applied. You go away and do it and do it as quick as you can given the number of CPUs that are available."
It's interesting there because today it's pretty easy for you to just say, "Here's the data." You can just give it a reference to some big array or some object or whatever. Specifying what the operations are would typically involve you giving references to pieces of code, if you will delegates or lambdas, and it sure would be nice if the compiler could analyze and guarantee that these lambdas have no side effects and warn you if they do. That's part of what I'm talking about, but that's just one kind of concurrency. There are other kinds of concurrency for more asynchronous distributed systems, which is a different kind of concurrency where we could also benefit from support in the programming languages. If you look at a language like Erlang, which is used in very highly scalable distributed systems. They have a very, very different model of programming that's much more functional and that's based on asynchronous agents and message passing and so forth. There are some interesting things that I think we could all learn from also in our languages.
Does the object-oriented paradigm create problems?
Anders: You know, it depends on what you group under the object-oriented paradigm. Polymorphism and encapsulation and inheritance are as such not a problem, although functional languages typically have a different view of how you do polymorphism with their algebraic data types. Aside from that, I think the biggest problem typically with object-oriented programming is that people do their object-oriented programming in a very imperative manner where objects encapsulate mutable state and you call methods or send messages to objects that cause them to modify themselves unbeknownst to other people that are referencing these objects. Now you end up with side effects that surprise you that you can't analyze.
In that sense object-oriented programming is a problem, but you could do object-oriented programming with immutable objects. Then you wouldn't have these same problems. That's kind of what functional programming languages are doing, for example.
Regarding your interest in functional programming, should computer science students study more math and experiment more with functional programming?
Anders: Well, I certainly think that it is important to include functional programming in any computer science curricula. Whether you start with it that depends. I'm not sure that your very first introduction to programming should be functional programming, but I definitely think that it ought to be part of a curriculum.
I got into programming not because I wanted to make lots of money or because someone told me to. I got into it because I just got totally absorbed by it. You just could not stop me. --Anders Hejlsberg
What lessons should people learn from your experience?
Anders: Well, if you look at the first product I worked on, Turbo Pascal, it was very much about not believing the traditional way of doing things. Don't be afraid. Just because people tell you it can't be done, that doesn't necessarily mean that it can't be done. It just means that they can't do it. I think it's always fun to think outside of the box and try to find new solutions to existing problems.
I think simplicity is always a winner. If you can find a simpler solution to something--that has certainly for me been a guiding principle. Always try to make it simpler.
I think to be really good at something, you have to be passionate about it too. That's something you can't learn. That's just something that you have, I think. I got into programming not because I wanted to make lots of money or because someone told me to. I got into it because I just got totally absorbed by it. You just could not stop me. I had to write programs. It was the only thing I wanted to do. I was very, very passionate about it.
You have to have that passion to get really good at something, because that makes you put in the hours, and the hours are the real key. You need to put in a lot of work.
Don't miss yesterday's excerpt from the interview with Anders on C# Language and Design.
If you enjoyed this excerpt, buy a copy of Masterminds of Programming.