John Carmack discusses the art and science of software engineering (2012)
blogs.uw.edu
blogs.uw.edu
I like this very much. For all the talk of power in languages, we make lots of mistakes. One underlooked decision factor in choosing languages should be, "How hard is to make accidental mistakes?" and "How easy is it to find and fix mistakes?"
One of the historical guidelines in C was "trust the programmer". This would be fine were it not for the fact that programmers, like all humans, are frail and fallible beings. Experience shows that whatever techniques are used it is hard to write "correct" software. It is good advice therefore to use tools that can help by finding bugs and preventing bugs. Ada was specifically designed to help in this respect."
https://www.adacore.com/adaanswers/gems/gem-30/
(And if even Ada is not restrictive enough, one uses Ada's subset Spark, e.g., http://www.spark-2014.org/ )
Not to say that one should use Ada (though it would often help I guess), but this different approach to software design and maintenance ("Ada culture"[1]) is worth knowing.
[1] http://archive.adaic.com/intro/ada-vs-c/ada-vs-c.html
My personal rule of thumb:
C and Scheme for explorative hacking, Ada and Haskell for reliable implementation, Perl for teh recreational lulz.
And it is a huge failure. No amount of runtime checks can make your already compiled and deployed program less wrong.
> (Taken from the Ada vs C++ comparison article) "Stroustrup (...) presents a use-defined vector class (...) Unfortunately, the proper implementation of the class requires exceptions (...)"
A proper implementation of the class requires dependent types. Runtime index checks are effectively testing, which can show the presence of bugs, but never their absence.
Sure, accidental mistakes are avoidable. But I don't think there is a way for the language to help us write proper abstractions. I mean if one doesn't know how to take mutable state from a bunch of functions and abstract its mutation away, i.e. allow it to mutate inside a single function only, than what can the language do? But what if you simply have an inconsistent code, like handling errors differently in different places?
Would you use a language that produces a warning of a hypothetical bug if you have state changes too far away from each other?
I'm not exactly sure what the solution is, but if history is any indication, these linguistic systems are often considered foul-proof to the point that those managing them stop managing them, or hand off management to someone and forget about it, letting any issue outside the scope of the language become a cancer of sorts that eventually kills the host.
Sadly, this is not true for us programmers. By and at large, we have preferred to devise mechanisms (such as exceptions and contracts) that protect us from the consequences of our mistakes, rather than attacking the root cause of these mistakes - our failure to enforce pre- and postconditions using static techniques. And this situation is not going to improve as long as we stick to a limited vision that embraces integers, strings, lists and trees, but not equality of terms as types.
Hell, this goes so far as to how we currently give people directions to cross a state. Consider http://goo.gl/maps/4F89Z This clearly doesn't work because there is not a car that can do that trip completely non-stop (well, that I know of). Yet, it would be silly to even try and account for all of the random stops that a person making that trip would want to do. How is this the fault of "embracing integers, strings, lists and trees?"
Having looked over all of the various ways that we give directions to kids lately, I'm becoming more and more convinced that the higher theories of programming are missing something (for a great presentation on this idea, see [1]). Look at how music is written. Look at how lego directions are written. Ikea directions. Shampoo directions. Cooking recipes. All of these are relatively successful methods for communicating often complicated sets of directions. None of them necessarily encode things to the level of detail that our programs require.
Are you concerned that all of my examples are not necessarily life and death detail oriented? See the blueprints of rockets. Sail boat designs from ancient times. Heck, sailing routes.
If anything, I would say that our programs should, instead of offering fancy ways to escape the consequences, programs should offer ways to "get back on the desired path" of what was supposed to be accomplished. The canonical "FileNotFound?" Don't put my program somewhere where I have to somehow set things back up to try again. Resume right here once the file has been found. (Sadly, I don't have any good thoughts on this. Just late night ramblings.)
[1] http://www.infoq.com/presentations/We-Really-Dont-Know-How-T...
I do think it's reasonable to have technical environments where language choices are done for support and debugging purposes, with the highest powered tools only pulled out on occasion.
https://news.ycombinator.com/item?id=4423031
Here is the link to the transcribed portion of the talk (starting at 00h:30m:02s):
My thoughts:
I probably would have intuitively grasped algebraic datatypes. It often frustrated me that I could not define new things like True and False to suit the game worlds I wanted to create.
I would have had little to know interest in math or recursion for its own sake. I'm pretty sure I would not have done well with those.
I would have probably written almost all of my code in the IO monad, with game rules defined outside.
Graphics programming in Haskell would have been no better than with any of the other languages.
Overall, I don't think Haskell would have stuck. I think I would have been easily frustrated, despite being able to structure programs better than I was able to in Basic. For proof of this, I look to my early abandonment of C/C++ for game development (even if the manuals were pretty much terrible). On the other hand, if I was presented a game written in Haskell (with source), maybe the outcome would have been different. I certainly managed to use a hex editor at that age, so if I had some goal I wanted to accomplish (like modifying the AI of a game), I think maybe anything would have been possible.
What I think made Haskell stick was that it was fun and easy to start write "real" programs with. :-)
When I think about it, Scheme is probably a great option for beginners. It's simple and functional, got a great development environment for beginners (Dr. Racket) and two great beginner books(SICP, HTDP2). Also, since it's not purely functional the user who learn it won't need to learn abstract concepts like monads to do useful stuff in it. :-)
The lessons of How to Design Programs are really well done, in the sense of reliably taking people from beginner to real programmer, yet everyone who's offered on opinion to me found it kind of tedious. I get the impression it's great for classrooms and there's a need for a resource like it aimed more at the self-motivated. Maybe that's http://realmofracket.com/
What they really need is a mentor, learning materials, and the drive to create something. The smartest kid in the world isn't going to build anything useful or interesting if he doesn't care about it. He'll be assisted big time if he has someone to give him a sense of direction. It's amazing how much a 'dumb' but determined kid can get done, even if underneath it's a horrible jumble of spaghetti code.
That's exactly how I started programming, with QBASIC gorillas, savegame hex editing, and Robot Battle AI programming. I eventually muddled my way through QuakeC, because it was for fun. It was only really at this point that I was ready for proper training materials.
Actionscript was actually great for me. I wanted to make flash games, and back in the AS2 days you could add code to frame 0 of movieclip objects. It was ugly, but it was the easiest way to get something to happen. It also tricked me into learning a form of object oriented programming.
Does it result in better programmers? Perhaps. It certainly forces you to understand recursion.
However people who'd never programmed before the Haskell course had a very hard time transitioning that knowledge to Java and similar languages, which was used in some other courses.
I didn't have any books or anyone knowledgeable to learn from at the time, but I was able to constantly look through that documentation to try to figure out what I wanted to do.
I had to make meticulous notes on everything, and make sure I really understood something in order to use it to solve a problem I was getting stuck on.
I think that hard-won knowledge was excellent, and it really taught me the value of API documentation and being able to find the answers myself just by reading that documentation.
Sure you can take short cuts by skipping discrete math and teaching the "act" of programming without the understanding. But in the end the seven year old isn't really learning programming then, is he/she?
I understand you can't teach everything upfront; that's not my point. I remember my 5th grade class, where several people said they wanted to be mathematicians when they grew up. The reality usually sets in by college, that all the 'math' we learned in the classrooms was just a tiny, tiny corner of a vast field of study. So similarly, I thought the 'programming' I was learning as a beginner was the real deal. When you're starting out, you never see the invisible, the unknown unknowns, the hidden complexity; you only see what's in front of you, and it has to make sense all on its own.
So there's this huge, intangible mess of complexity that is 'math' or that is 'programming', and you can't explain it all at once. They won't have any of the prerequisite knowledge, and you can't waste time covering all that. So you have to choose 1 concept out of the 7000, and you have to try to teach it.
There could be C programmers that know nothing about monads, so it stands to reason you could teach lots-of-C before you teach anything about monads. Is the opposite true? Are there Haskell programmers that know nothing about pointer arithmetic?
But I feel that the difference is between knowing and understanding. You can know how to get the browser to display a rectangle with the right copy/pasted html.
But without discrete math there really is no foundation to understand the underlying algorithms. Without understanding it is impossible to know how to extrapolate those examples to solve different but similar problems, probably the most important skill a programmer can have.
Teaching C before discrete math is like teaching english without a vocabulary. Sure you can understand the syntax. But without understanding algorithms or data structures you are going to be lost very quickly. You might not need monads for C - since it is such a primitive language. But try writing C without iteration or recursion.
There seems to be this ineffable force tilting the scales to create more C veterans that don't know monads (I could find you one) than there are Haskell veterans that don't know pointer arithmetic. Age, popularity, libraries, etc all tie into this, but I still think there's significance amidst the noise.
How does he manage to look at this pile of hacks with beginner's eyes? Somehow he does, and he sees it for what it is without judging it too harshly or prematurely. I love reading his blog.
There's some more, somewhat old and non-technical, information about NASA's software development efforts here [1].
I think Carmack's talk shed light on the fact that as humans we have very limited cognitive capacities when it comes to programming and "smarter" languages help us a bit, but we need automated, "smart" tools - like static analysis tools.
I think of Visual Studio and Resharper and how much ReSharper finds that I can learn from and how it frees me to concentrate on business logic.
Years ago I remember reading an article or an interview with a Sun research scientist where she described programming of the future where languages and tools will be much more lenient of your mistakes and programming will be much more of a two-way conversation with your tooling.
That and tools like language work benches are the only ways I see to go forward. Everybody using Haskell is just a modest (if that) step forward.
Do you have any experience programming in Haskell? I ask because this statement exactly describes my experience with it. The language goes extremely far down the path of "static analysis" with its type system. Couple this with simple tools such as ghci, flymake and haskell-mode for emacs and you have a very interactive system with an enormous amount of feedback.