Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Programming Modern Systems Like It Was 1984 (dadgum.com)
142 points by spiffytech on Dec 3, 2014 | hide | past | favorite | 80 comments


1984: MacOS GUI programming in (Lisa)Pascal, Lightspeed Pascal came the following year, and the MPW with Object Pascal a couple of years later.

OOP wasn't very common yet, but when you look at the Mac Toolbox, you can see that it is definitely an OO design (eg. Window is a subclass of GrafPort and Dialog is a subclass of Window, see also the various Control and "subclasses").

Therefore I would say that 1984 with the Macintosh starts modern programming age, marked with high level programming languages (the Mac Toolbox was written in Pascal! (only the Mac OS was written in Assembler, and of course Quickdraw was first debugged in Pascal, and then hand-optimized in assembler), and with object oriented programming.

Of course, when people started to write Mac applications in C, it was a set-back, but then NeXTSTEP made a big advance with Objective-C and OpenStep (now Cocoa, ie. MacOSX and iOS), which allowed the creation of the web (and therefore Javascript).

But really, OpenStep (Cocoa) is just a cleaned up version of the Mac Toolbox (which was itself a dumbed down version of Smalltalk developped in the 70's at the PARC).

So I would say that not much new since then. We've heard of some experiment where some program was run on the Amazon cloud to debug programs on a cost of $8/bug (which assumedly is cheaper than a programmer debugging), but we don't have those kind of AI aids integrated in our tool boxes. We've heard of some automatic proof systems (Coq, ACL, etc), but management wouldn't let us invest in their use (perhaps rightly so, they seem to be as much time sinks as programming itself).

We ARE still programming like it wask 1984!!! (unfortunately).

Well, not exactly, ok. Now compilations are instantaneous instead of taking hours, and just try to write your next program on a 512x342 pixel screen (or just a 80x25 terminal) with no Internet access for the documentation (all the documentation printed in thick bibles on your knees! (Imagine Android documentation printed!)).

Ok, there's some progress still: nowadays I use Common Lisp and emacs all the time (for a pale rendition of the 80's Lisp Machines), and people often use Ruby or python, or even Java, which are run with garbage collectors. So this invention from 1959 finally takes ground, helping us concentrate on solving users' problem instead of managing computing resources.


Started coding in 1986. So no cryogenic sleep, rather living through the whole thing.

Agree with all those points.

Actually C was already primitive in 1984 and only mattered for those coding in an UNIX system. Meanwhile, some of us had the chance to Modula-2, Ada and the myriad of Pascal dialects that were available back then.

Always with an healthy set of Assembly code, given the quality of generated code, even in C's compilers case, which were everything but fast back then.


I go back to '81, and the bigger pix missed is the staggering homogeneity and low cost.

"What? The compiler is free, not $299.95 mail order? And the same compiler runs on your desktop as on your server? What manner of sorcery is this?"

"The worlds best programming editor is free?"

"Wait, you mean the same kernel and similar OS is on my phone and my settop box and my desktop and my server?"

The lack of "NIH" and pounding square pegs into round holes is spectacular.

There is a bit more attention to security now that everything is interconnected. When the default is never to be connected to anything, you only need security slightly better than physical security which usually isn't much.

Subjectively, error messages are staggeringly more verbose yet contain less content than ever. I'd like to see an AI system for error messages that provides more useful error messages. AFAIK this has never been implemented.

Another subjective annoyance is believe it or not in the 80s we knew all about no silver bullet and all that, and we were dumb enough to think the industry would implement the lessons and workarounds instead of making the same old mistakes over and over. In the 80s, Brooks had published his MMM a decade ago about experiences two decades earlier and sure, software projects in the modern mid 80s sometimes did dumb things right out of the pages of MMM, but surely in the 90s, 00s, 10s, and 20s no one would be that stupid.

Another interesting 80s point of view is recent CS research topics like sorting was more or less applicable to line of business programmers. The red black tree data structure was only a decade old. People were researching performance bounds on slightly older stuff like the quicksort right up to the 80s. In the 10's its very unusual for any fundamental CS discoveries from the last 30 years to cross the desk of your average IT drone other than cryptography (which is usually misapplied such that the average IT drone doesn't do crypto correctly anyway), which was not the case at all in the 80s. We may have been working out of couple year old books instead of minutes old web pages, but we were working within a decade of the cutting edge, not redoing stuff from decades ago but now on a webpage instead of CICS.


"Actually C was already primitive in 1984 and only mattered for those coding in an UNIX system."

There was lots of code getting written in C on IBM PCs (running DOS) back then. This article in Byte Magazine from 1983 reviewed nine different PC C compilers:

https://archive.org/stream/byte-magazine-1983-08/1983_08_BYT...


C on MS-DOS was an option among a myriad of languages. MS-DOS was written in Assembly. All major compiler vendors had C, Modula-2, Pascal dialects, BASIC dialects, Assemblers to choose from.

I only looked at C for the first time in 1993 and was dysmaid what it offered vs the alternatives.

C on UNIX was The Official System Programming language.


I'm setting up a DX4 for messaround with Norton books and asm at the moment, so quite interested in this. The general purpose environments I know of from then would have been C, Turbo Pascal, Basic, ASM, and then probably some databasey things like Clipper, Foxpro. What do you feel was stronger for general purpose dev in that era than Turbo C? Even on OS/2 2.0/1, I can't think of a general purpose tool that would have been more effective than Visual C (but I wasn't actively developing myself then).


Back then the "default" languages for making dos programs were Turbo Pascal & QuickBasic. C was the 3rd place I guess.


4th place.

Assembly came before it. MASM and TASM were quite good.


Here's the C compiler I used back in the day: http://www.mixsoftware.com/product/powerc.htm

Also, Turbo Pascal had easily the best IDE for the day.


Turbo Pascal, of course.


OK, given, that's pretty cool. Although Pascal/Ada would be nicer if they had shorter tokens.


I always favored readability over hieroglyphs.

Code is read more times than written. Specially important when you need to act as fireman in foreign code, a common situation in consulting.

Check Turbo Pascal 6.0 or 7.0 with Turbo Vision.


Hieroglyphs are not necessarily less readable than words. It's good to be able to build mental state, and the more verbose a language is, the more you need to dance between parsing mode and building mental state. Context shifting is expensive. Paging around is particularly exhausting.

I spoke to one of the guys working on the K-language OS at a conference last week. He made the case that - once learnt - the grammar for K it is objectively simple to build mental state from compared to alternatives. I'm open to this being true.

I didn't know about Turbo Vision - that's great. And cross-platform too. Thanks!


Someone should make a front-end (preprocessor?) for Ada or a Pascal with braces et.al. Then publish it as a brand new systems programming language. (Optional: "inspired by Go".)


When I got to use C, the first thing I did was macros for new() and dispose() as wrappers for malloc() and free().

I couldn't understand that C compilers were so primitive that they forced each developer to give how much memory one wants to allocate.

On the same year (1993), C++ was my escape hatch from C pain with a bit of Pascal safety back, since Turbo Pascal was stuck in PC world and everyone was ignoring the new Extended Pascal standard.

EDIT: typo whats => wants


The problem with Pascal was that it didn't have strongholds like C/C++. It was a good (better) general purpose language than C or C++ but then you needed inline ASM for more things as it didn't allow for pointer arithmetic. Borland dominating Pascal practice rather than any standards didn't help either, it just didn't translate well. When Borland withered it took Pascal/Delphi with it.

At the end of the day success is attained in a market. This is basically a messy evolutionary system that is far from perfect and leaves a lot of noisy vestiges in its path, along with unjustly sacrificed unsung heroes. Look at Java, C++, the mobile platforms, etc. What a fucking mess. But it is the way it is.


You could do pointer arithmetic in Turbo Pascal, but I agree with your overall description.

That is why I eventually moved to C++ back then, it might have some warts, but feels way better than C in terms of safety and modular programming support.

Nowadays I spend most of my time in JVM and .NET worlds.


IIRC pointer arithmetic in Turbo Pascal had some restrictions vs C (typing, limits). Still nothing of that was standard Pascal at least back then.

You willingly spend your time in JVM world? ;-)


You could hardly do anything in standard Pascal, that is why we were all using dialects and Extend Pascal was produced.

> You willingly spend your time in JVM world? ;-)

Of course. When it appeared it was a good alternative to a dying Pascal eco-system and the chaotic world that was C and C++ compilers trying to catch up with the standards.

Now Oracle is recognizing that Sun made JNI hard on purpose and Java 9/10 are looking good with the missing features to be more hardware friendly.

Anyway, on my line of work customers get to say what they want on their IT stack and that has been Java and .NET since around 2005 (on our case).


It used to be that the major limitation in computing was hardware. Look at the creativity that it spawned. We are still living off of ideas three decades old.

Now what is the limitation? Is it programmer stupidity? Or maybe there is no limitation? Maybe that is the problem.

History shows hardware kept getting faster, but there has been no decrease in the amount of ever larger, slow, unreliable software. Where is all the lean, "instantaneous" software running in RAM, extracting every last bit of computing power, instead of mindlessly consuming it?

What exactly is the point of "programmmer productivity" and whatever problems some programmers think that this justifies?

Is the point to advance computing? The state of the art?

Or is the goal is to peddle their junk to those with very low expectations, not to mention those with zero expectations. (The later were not old enough, or even alive, to see how computing was done in the 80's and hence have nothing with which to compare.)

It is unfortunate that giving programmers what they wanted -- faster hardware -- has not resulted in software that is any more creative or powerful, and truly it is less so, than the software of the past. Only hardware has improved.

Given the history so far, I would argue that the only proven path to true creativity in computing is through limitation.

Perhaps present day computing's limitation is programmer stupidity, or to be more gentle, programmer ignorance.

Agree 100% with all points in the blog post, except perhaps the last one. Well done.


It used to be that the major limitation in computing was hardware. Look at the creativity that it spawned.

This can be summed up in one word: demoscene. In fact I thought the article would be about the demoscene, just from its title.

History shows hardware kept getting faster, but there has been no decrease in the amount of ever larger, slow, unreliable software.

That's what I think someone who took this 30-year-leap would perceive if they switched from a computer of the 80s to a new system today: "This app is how many bytes!? It's pretty and all, but it can only do this? Why does it take so long to boot up?"

Interesting comparison here: http://hallicino.hubpages.com/hub/_86_Mac_Plus_Vs_07_AMD_Dua...

What exactly is the point of "programmmer productivity" and whatever problems some programmers think that this justifies?

I think it's more like a combination of laziness and selfishness: programmers want to be more "productive" by doing the least work possible, and at the same time are uncaring about or underestimate the impact of this on the users of their software. Trends in educating programmers that encourage this sort of attitude certainly don't help...

There's also this related item a few days ago: https://news.ycombinator.com/item?id=8679471


Now what is the limitation?

Latency. Our hardware may be getting faster and faster, throughput-wise, but it is certainly not improving very much in the latency department. Many of the things we'd like to build (such as AI) tend to have a lot of dependent memory accesses. These sorts of processes are highly sensitive to latency.


Learn to trust virtual memory. This is the real reason not to use temp files. Virtual memory handling is mature in all modern operating systems.


In some ways it's still 1984, especially for mobile dev:

> Highly optimizing compilers aren't worth the risk.

Rewrite slightly to "non-mainstream toolchains aren't worth the risk". Should you write your next game in pure Java, or use JNI/C++, or use Unity, or something more obscure? No matter which you pick, you'll be running to StackOverflow (if you're lucky) to fix the periodic show-stopper. Using the most popular tool gives you the highest likelihood of success without delving into library source code.

> Something is wrong if most programs don't run instantaneously.

Forget startup time -- Make a 1980s developer deal with iOS code-signing issues and watch them switch their occupation to animal husbandry.

> Everything is so complex that you need to isolate yourself

Back then documentation was delivered in thick paper volumes which often contained more bits of information than the software being documented. Now we just Google StackOverflow and cross our fingers.


In some cases, 2x or 4x is worth the risk. Imagine having a warehouse-scale computer where you can use 50,000 CPUs instead of 200,000.


'In some cases' is the key bit. In the 80s, it was much closer to 'all cases'. Nowadays, by all means, turn on the optimizing stuff if you need it, but you are adding risk (especially since you are probably using something like C if you're that concerned about performance, and with so many undefined behaviors)...but unlike the 80s, there's a decent chance you may not need it to achieve the performance goals you need.


Gah. An interesting thing to think about, but you do have to actually think.

> Highly optimizing compilers aren't worth the risk.

If they were buggy, sure. But, probably through the weight of every single developer using them for absolutely everything, the bugs have been ironed out.

> Something is wrong if most programs don't run instantaneously.

Why? Do a straight-up cost/benefit analysis. How much extra would you pay for a program that ran twice as fast? If anything I'd expect people pay more for bigger programs that take longer to run, since they feel like they're doing something important.

> Design applications as small executables that communicate.

The microservices people are sort of doing this. It's a great way to turn all your problems into distributed problems. And what's not set up is any sensible way to send even basic data structures around. "pipes and sockets" send streams of bytes. In 2014, if I want to send an object, a function even - heck, if I want to send an inert struct - from one process to another, I'm basically stuck. The least-bad option is probably to write out the object definition in Thrift IDL, generate a bunch of code and then write a bunch more code to map back and forth with my actual program datatypes. It's hard to express quite how much it sucks, but many people have tried and failed to come up with something better.

> Don't write temporary files to disk, ever.

If you're building new executables for everything you're either doing something wrong, or you don't care about the space you're wasting. The right way to do these tiny tweaks is in an interactive, workbooky environment like ipython. If you like that style of programming.

> Everything is so complex that you need to isolate yourself from as many libraries and APIs as possible.

Absolutely backwards. Everything is so complex - and using libraries so easy - that you should use libraries for everything. A good library provides a simpler interface than the things it's built on top of, so you can just call e.g. encrypt(data) rather than doing thousands of lines of bit-twiddling.


> > Something is wrong if most programs don't run instantaneously.

> Why? Do a straight-up cost/benefit analysis. How much extra would you pay for a program that ran twice as fast? If anything I'd expect people pay more for bigger programs that take longer to run, since they feel like they're doing something important.

This is one of the things going on with Go. What's the cost/benefit of a sub-second compile time compared to a ten-minute compile time? Not all that much, really. But when you multiply it times multiple team members, times multiple daily compiles, times a code base that's going to live for the next 20 years, those slower compiles add up to a big waste.


Eh maybe. I don't find compile time is ever a limiting factor, but if it became one there are ways around it (e.g. incremental compile servers). And sacrificing a small amount of development time for fewer errors is almost always a good tradeoff.

(What's your estimate of the cost of maintenance on the repetitive code that Go's inadequate type system makes necessary?)


That's hard for me to say. Can you give me a concrete example of "the repetitive code that Go's inadequate type system makes necessary"?


I'm starting from an assumption that keeping track of effects is necessary, and that these effects are things that aren't built into the language. E.g. database access, structured logging (like an audit trail). (You're allowed to say that you wouldn't want to carefully keep track of these things, that it doesn't matter whether a particular function accesses the database or not, but in that case rather than a repetitive code cost, I'd say you end up with a testing/safety/maintenance cost). An example is necessarily pretty abstract or complex, because it's only when you're doing these complex things that you really need to abstract over them at multiple levels. But I'll try:

One of the fun things I can do in Scala is build up an "action" using for/yield syntax:

    for {
      f ← auditedFunction1()
      g = nonAuditedFunction(f)
      h ← auditedFunction2(g)
    } yield h
I can do the same thing for database actions:

    for {
      results ← myQuery()
      modified = results map myTransformation
      saved ← modified traverse save()
    } yield saved
And then I can have my Spray directives know how to handle a generic action, by writing a simple instance for each action. E.g. I can tell it to display the audit trail if a particular header is present:

    implicit def auditOnSuccessFutureMagnet[T](
      inner: Auditable[T]) =
      new OnSuccessFutureMagnet {
        type Out = AuditTrail \/ T
        def get = header(MySpecialHeader) map {
          case Some("mySpecialValue") =>
            -\/(inner.written)
          case None => \/-(inner.value)
      }
And I can do a similar thing for database actions, wrapping them in a transaction at the HTTP request level - giving me the equivalent of session-creation-in-view, but tied to the actual request rather than a thread, and with safety maintained by the type system.

All of this stuff is typesafe. I can only use a contextual action that I can provide an implementation for how to handle (there are a few defaults like e.g. Future), and I can only return an object "inside" the context that I've also provided how to handle (e.g. by providing a json formatter for that type). And none of this is special or built-in (other than the OnSuccessFutureMagnet interface that I'm implementing); all these things are custom classes or from different libraries.

How could I do that in Go? I'd have to declare each of the different combinations (each possible DatabaseAction[A] and also each possible Auditable[A]) as separate types, right? And my framework certainly couldn't offer the generic handling of F[A] that lets me reuse the same functions for both, so I'd have to have separate copies for anything that I wanted to work with both DatabaseAction and Auditable.


It's hard to express quite how much it sucks, but many people have tried and failed to come up with something better.

Erm, ZeroMQ? nanomsg? HTTP?


> ZeroMQ? nanomsg?

Still oriented towards streams of bytes, no? Frankly I don't trust the implementation based on some of the blog posts, but even if I did, the part of the problem they solve (exactly-once delivery) is one I prefer to solve in other ways.

> HTTP?

Can work, but still ultimately not structured enough; it's hard to evolve a HTTP API in a verifiably compatible way, and you usually end up having to maintain some kind of schema or IDL and explicit conversion code - at which point Thrift provides a standardized way of doing it.


All of those options provide message-centered data flows. You have to define the message format--JSON, BSON (lol), or even protobuff would work fine.

Just because you refuse to use well-established tools doesn't mean they aren't there.


The serialization part - JSON, BSON or protobuf - is the part I'm complaining about. I have plenty of perfectly good ways to copy a packet of bytes from A to B. I want something that offers the safety guarantees of protobuf with a "simple case" that's as easy as e.g. Jackson. And I want something that can send functions, not just inert structs.


So why do HPC system developers still use the 1k /seat intel Fortran compiler then?

Becase IPC/Kw is an much more important metric thesae days


The HPC folks work in the niches where people will pay extra for better performance; those niches do exist. But they are small niches.


> but many people have tried and failed to come up with something better

Can you give any examples? I'd be interested.


CORBA. Java RMI. EJB. To a certain extent Erlang (which may yet succeed), or the various Lisp S-expression serializations.

(My view is that turing-complete code is the wrong abstraction and we need to draw an explicit distinction between serializable code and non-serializable code, but that would require a programming-language-level solution and a rewrite of approximately everything. Sooner or later I'll get around to writing a language based on this notion)


Great write-up. I like the perspective of a 30 year jump. In retrospect it feels like we are lobsters slowly boiling. When looked at from a distance, these observations become much clearer.

> It's time to be using all of those high-level languages that are so fun and expressive

I think for most of us, programmer efficiency is more important than program efficiency. IMHO, in these cases, there's much to be gained from the scripting languages. For many programmers though, these seem to push some comfort zones too much. There are obviously some domains were performance is still king and the high-level languages won't cut it.

> Highly optimizing compilers aren't worth the risk.

For some they sure are. Probably would have been better stated with "not worth the cost" rather than risk. I don't see much risk here, more so cost (in the terms of effort and opportunity cost). I'd rather not worry about the details of bit twiddling on things if my current problem is plenty efficient on the small n's I'm dealing with. Compilation time can be a real cost as well. I have much faster code-test cycles in scripting environments.

> Something is wrong if most programs don't run instantaneously.

I think many people don't comprehend how true this is. Its truly mind boggling to consider how many instructions a second are run these days. What could your program possibly be doing with all of them in one second? Again, for some folks there are very good answers. For most of us, its loading and initializing layers upon layers of classes. I routinely see some stack traces dozens of lines deep. I'm sure each one is providing some useful abstraction of something, but.. really?

We have a Java based application that was written without any external libraries, just straight up Java. It compiles down to a 120KB jar and provides significant real-time functionality with phone systems. Meanwhile some of our Java based web apps provide a 50MB war, but probably use every relevant Java library out there to help.

> Design applications as small executables that communicate.

Yes! I've gotten a lot of mileage out of this. Many would to well to read Eric Raymond's book: "The Art of Unix Programming". The only thing I'd like a better solution for is a nice mechanism to set-up and configure the deployment of said "small executables that communicate". I'd like programming to be as easy as building chain of commands in a Unix command line. Yet setting up a production environments with chains of commands seems a bit.. ugly.

> Don't write temporary files to disk, ever.

Once upon a time, those in the know would set-up RAM disks. These days with SSDs, it seems that's effectively what we are doing. Probably a negligible gain to use RAM over SSD. Only slight advantage of RAM over SSD I see is that things get cleaned up on a reboot (does anyone still reboot regularly?).

> Everything is so complex that you need to isolate yourself from as many libraries and APIs as possible.

I think this is behind the recent backlash against frameworks. Unfortunately, many raw languages still lack some basic utilities that make it just a bit painful to use without any libraries. Seems many people will be migrating towards unobtrusive libraries that add value in the places needed without requiring a cascading set of components and tight coupling with the application being coded. This is a good thing.

> C still doesn't have a module system?

I've got nothing here. In general, I've been migrating towards a more data driven approach rather than native code. I wish we'd start loading code modules the same way we would load a piece of data from a file or database. At the end of the day it's all the same. Obviously you need to be sure of the sources you are loading code from, but you should be protecting your data the same way.

UPDATE: Minor editing for clarity. UPDATE2: And math.


Optimizing compilers are pretty magical. It's easier than you think to code undefined behavior. When the compiler detects undefined behavior, it can do anything.

Here's a pretty neat example [1]. There have been a few writeups on HN over the years, i can't find some of the more elaborate surprising behaviors.

[1] http://blog.llvm.org/2011/05/what-every-c-programmer-should-...



You don't need an optimizing compiler to cause undefined behavior, though. Something as simple as printf("%f", 3) will do. Optimizations can expose undefined behavior that otherwise would remain unnoticed... but they can also "fix" undefined behavior that shows up without optimizations. They're really orthogonal.


>Once upon a time, those in the know would set-up RAM disks.

I still do. Also, most Linux systems I have seen have a few tmpfs directories by defualt (/dev/shm, and /run are common).


30 year jump


> why isn't it possible to create and execute a script without saving to a file first

It is certainly possible in Bash, at least:

  CODE='echo Hello; echo World'
  RESULT=$(bash -c "$CODE")
  echo "$RESULT"
Very good article though! :-)


Other more "obscure" options also come to mind: Emacs Lisp, Smalltalk, etc. Wasn't there also something in Plan 9's GUI like this? The whole saving to a file first thing is just a convention, and one that got me thinking: it's a convention for a reason (reproducibility), and this argument ("why can't I just run it without saving first?"), seems kind of contrary to the whole point of the post. Disks are fast! Saving a file first is ridiculously fast in this day and age. Just bind a keystroke to save and run in one button press. It speaks more to lousy development environments than anything else if you can't make this a one button press operation.


I started working part time while going to school in '85 (off by one year, here).

I did use a reasonably high level language, dBASE, rather than C/Pascal (most of the time). Writing a CRUD app in dBASE/xBASE (Clipper, Fox) back then, vs using a low level language, felt like using Ruby on Rails does now (vs "Enterprise" make-work). Of course, in both cases, you sometimes run into performance/capacity issues that make you rethink what you did with the higher level platform :-)

So, I guess I disagree that everything was done at or near the assembly language level back then.


I don't have to imagine what a programmer doing this jump would do, I can look to my father, who started out writing programs on punch cards in high school to be sent to the university, and maybe a week later he'd get results back (if his program worked at all). He retired from his day job writing Java a few years back, and is still hacking on websites using Tomcat, etc, and he's never looked back.


How it might go:

"That web programming thing sounds pretty fun!"

(glances across bookshelf)

"So if you'll give me a copy of the spec, I can get started."


So that's where Bernstein comes from.

This advice has a lot in common with his (& others) advice on how to write safe programs with a small trusted base---and the other elements seem a matter of shared taste.


> Why does this little command line program take two seconds to load and print the version number?

> Don't write temporary files to disk, ever.

> Why does a minor update to a simple app require re-downloading all 50MB of it?

Gets me!


> C still doesn't have a module system

There's package manager-ish: https://github.com/clibs/clib


How does it solve naming clashes?


High level, instant startup, message passing, isolation from complex APIs (interfaces), and a good module system. Sounds like he's advocating for Go.


Temp files are great - you can inspect your output. And, while they might suck on Windows, on Unix, they are pretty much an in-memory data structure anyway.


On Ubuntu (14.04), /tmp is part of the root filesystem. /run seems to be the main ramdisk.


What I meant is that a "temp" file may very well never hit the actual disk, rather than memory buffers, until after the time that app 1 writes it and app 2 reads it.


Several issues with this post:

> It's time to be using all of those high-level languages that are so fun and expressive

But then soon says:

> Something is wrong if most programs don't run instantaneously

Well, most higher level languages are getting interpreted, which require some cpu time to startup. Even with something not-so-high-level-but-sorta-high-level like Java or C#, you still have an abstraction level (JVM/CLR) that must first boot, then interpret, then execute.

> Highly optimizing compilers aren't worth the risk

Today's optimizing compilers are truly works of genius, and can perform optimizations on code un-thinkable by most programers. Even if they were thinkable, the source code required to produce the same machine code without compile-time optimizations would be hairy to say the least, and mostly unmaintainable (unrolling loops, bit-shifts instead of multiplication, etc). Not to mention 2-4x performance improvements is nothing to shake a stick at.

> Design applications as small executables that communicate

I don't have any particular qualms with this statement in principle, however with some enterprise apps this simply isn't feasible. Sure I may use a great many separate apps, all working together in orchestration (my database, authentication server, some 3rd party libs, etc), however for some apps, a great deal of the codebase will end up being a monolithic piece, and that is just fine so long as it's maintainable and extensible.

> Don't write temporary files to disk, ever

This depends on what the temporary file is (and how "temporary", temporary really means in context). If I'm writing a program that will generate some XML document then ultimately send that off to some remote server, I might want a local copy kept in the working directory so that in the event something goes wrong, I can look at the file and see what the last produced content was (or wasn't).

> Everything is so complex that you need to isolate yourself from as many libraries and APIs as possible

This seems to be in the context of writing system-level code. Even in that context, if a library has done something already that will be useful to you, use it. Taken to the extreme, this could imply one should re-write portions of libc into their app to avoid any external API calls in a vein attempt to guard against external dependencies and a potentially changing API surface. In my projects, I typically have a loose rule that if something is relatively maintained and recent, and it helps me get my job done quicker, I'll use it. That is a very loose definition, but typically prevents me from including some lib that hasn't been updated since 2002 just to parse some XML document.

> C still doesn't have a module system

Any why should it? It's a low-level language usually (but not always) reserved for low-level (ie. system) programming. This is not the typical language of choice to write your pluggable enterprise app in.


> Even with something not-so-high-level-but-sorta-high-level like Java or C#, you still have an abstraction level (JVM/CLR) that must first boot, then interpret, then execute.

There's no reason those shouldn't be resident daemons. Heck, if they were, why should the OS be anything other than a hypervisor for managing those, plus some abstractions like a "container" interface that each runtime execution driver implements? We could have had something like Microsoft Research's SingularityOS a decade ago if we had approached the problem from the other direction—pushing the runtime lower in the stack, instead of trying to write a kernel that does everything. Imagine your laptop booting into Xen, and then loading a Smalltalk image that contains the desktop, while in parallel booting up some background daemons into virtually-isolated Erlang nodes.

> Sure I may use a great many separate apps, all working together in orchestration (my database, authentication server, some 3rd party libs, etc), however for some apps, a great deal of the codebase will end up being a monolithic piece, and that is just fine so long as it's maintainable and extensible.

The author's complaint was less about factoring of components and more about the way we (still) do IPC. As in, why are we directly-and-unsafely sharing allocated memory handles between families of processes (i.e. using threads) instead of just having processes push information to one-another's inboxes over sockets? Anything that's currently threaded could be done with message-passing instead. And like the article said, it'd be only a 2-4x difference in overhead, minuscule compared to the speedups over the past decades.


> There's no reason those shouldn't be resident daemons

Well, you can blame OS vendors for that. But would it be practical even if implemented? For a system that will never execute Java code, why would a JVM daemon be running in the background? Even if it were a daemon, it would still require spool-up time to interpret the first byte-code down to machine code before the built-in jvm optimizing compiler could kick in and compile down to native machine code.

> hypervisor for managing those

We do have similar things now-days, but it's worth considering that your OS does a lot more than just simply execute other code. But again, there's a practicality issue trade-off. Containers aren't exactly easy, and it's unlikely that even today's implementations will be the last attempts that get it 100% right. (BTW, Xen requires a Kernel, and for the sake of argument, your laptop does not require anything but the kernel to boot (actually less, you can boot raw binary machine instructions), the rest is convenience for the user).

> why are we using processes that share mapped memory allocations (i.e. threads) instead of just having processes push information to one-another's inboxes over sockets

Sockets still have overhead. IPC can be achieved in a number of ways, some better than others, and some better in certain circumstances. Why bother opening a socket when my program consists of only a small handful of child processes all running on the same host and only require very small amounts of communication (such as a keypoller subprocess)? Message queues are fine for sending basic messages around, but even a message queue is unnecessary if I can pass the info into the child process as I spawn it. Why over-complicate things if not necessary?

> a 2-4x difference in overhead, minuscule compared to the speedups over the past decades.

That is, of course, relative. I think some large projects would argue a 2-4x improvement in compile-time, for example, is a dramatic time savings. If we snubbed every 2-4x performance improvement, we'd still have performance on the scale of 1984.


> Even if it were a daemon, it would still require spool-up time to interpret the first byte-code down to machine code before the built-in jvm optimizing compiler could kick in and compile down to native machine code.

The first time you loaded that bytecode, sure. Every time after, it should be hashing the byte code it's being told to load, and then taking that hash as a key into a disk cache of already-profiled-and-JITed object code. In fact, the application can ship with those cache files (like a pNaCl binary shipping with its native counterpart), or the developers could dump (signed copies of) those cache files into a DHT which the runtime would look in as an extended cache.

Either way, the API is still "load this byte code", but the bytecode is effectively just a really long key that looks up the real code that should be loaded. It's just that in the absolutely-cold path, the JIT (effectively the load-time compiler) can kick in and actually parse the bytecode into the required native code.


The OS/400 mainframes use a JIT in their kernel.

All userspace code, including C, compile to bytecode known as TIMI.


Depends what version you are running and the applications running ontop. The AS/400 box at my company runs a load of RPG language code, which is interpreted at runtime.


It's very likely that your CPU interprets the machine code at runtime. What's your point?

EDIT: "machine" -> "machine code". Doh.


There's no reason those shouldn't be resident daemons.

In fact, they already can be. For the JVM, there's nailgun[1], for example.

[1] http://martiansoftware.com/nailgun/


> why should the OS be anything other than a hypervisor for managing those, plus some abstractions like a "container" interface that each runtime execution driver implements? We could have had something like Microsoft Research's SingularityOS a decade ago if we had approached the problem from the other direction—pushing the runtime lower in the stack, instead of trying to write a kernel that does everything. Imagine your laptop booting into Xen, and then loading a Smalltalk image that contains the desktop, while in parallel booting up some background daemons into virtually-isolated Erlang nodes.

With Microsoft partnering with Docker, I can see exactly that happening in a few years.


> Well, most higher level languages are getting interpreted, which require some cpu time to startup. Even with something not-so-high-level-but-sorta-high-level like Java or C#, you still have an abstraction level (JVM/CLR) that must first boot, then interpret, then execute.

Anyone with a degree in CS knows this is not true.

Additionally there are quite a few native code compilers for Java and C#.

>> C still doesn't have a module system

> Any why should it? It's a low-level language usually (but not always) reserved for low-level (ie. system) programming. This is not the typical language of choice to write your pluggable enterprise app in.

I don't know? Maybe because every other system programming language since the late 70's had a module system?


> Anyone with a degree in CS knows this is not true.

Not sure what you are trying to imply here? Perhaps it's to say that all code can be compiled down to machine instructions? -- Well, that would negate a lot of benefits of using some of the higher level languages and/or their portability of architecture claims. So yes, my statement stands true unless you do compile down to machine code for your target platform.

> Additionally there are quite a few native code compilers for Java and C#.

I'm not very familiar with C#, but it's worth noting that Java does indeed get compiled down to native machine instructions during runtime (mostly noticeable on long-running applications and especially in "hot zones" of code). It's been claimed that the optimizing compiler embedded in the JVM is one of the best around, and usually will result in code running at the same performance level as C on the same platform (again for long running code allowed).

> I don't know? Maybe because every other system programming language since the late 70's had a module system?

There aren't exactly many languages that are considered to be "System Programming Languages". For most of the *nix's (all?), it's C (which as noted does not have a native module system). For Windows, it's mostly C++, which also has no native module system. One should ask what benefits a module system would bring to a mostly bare-bones language like C that could not be achieved in the 40+ years it's been around?


Ada, Eiffel, D, Go, Rust, OCaml, Common Lisp, Haskell, Scheme Dylan, among many others, provide native ahead of time compilers in their default toolchains.

All look pretty high level to me.

C# can be compiled ahead of time with ngen since .NET 1.0.

Singularity compiles Sing# (an extension of C#) to static binaries

Windows Phone applications get compiled to native code on the store. The mobile phones only see native code.

.NET Native will bring the same functionality to desktop applications.

Mono compiles C# to native code in iOS and offers the same functionality on other systems.

The now defunt CosmoOS used C#, compiled to native code, as their systems programming language.

There isn't no such thing as "The JVM". It is a specification with a reference implementation, that any JVM vendor can be certified against.

Just like any other language vendor can be certified against the respective ECMA or ISO standard for their respective languages.

Aonix PERC, JamaicaVM, J9, SubstrateVM, JikesRVM, ART are all examples of Java environments that offer the option to compile straight to native code.


> Ada, Eiffel, D, Go, Rust, OCaml, Common Lisp, Haskell, Scheme Dylan, among many others, provide native ahead of time compilers in their default toolchains.

I think you and Alupis have different definitions of the term "system programming language". In general, the ability to be compiled to native code is necessary but not sufficient for typical system programming language tasks, like hardware drivers, OS kernels, and language runtimes.

I think a good indication for a system programming language is the ability to write its runtime system in itself. Any language with non-optional GC collection fails this test, because you couldn't write the GC in that language.


> In general, the ability to be compiled to native code is necessary but not sufficient for typical system programming language tasks, like hardware drivers, OS kernels, and language runtimes.

No, because I have listed Ada in my list.

> Any language with non-optional GC collection fails this test, because you couldn't write the GC in that language.

Better improve your CS skills. Ever heard of Cedar, Modula-3, Oberon, Active Oberon?

EDIT: Just for the kicks, here is Oberon's GC implementation, in Oberon!

http://www.inf.ethz.ch/personal/wirth/ProjectOberon/Sources/...


>> In general, the ability to be compiled to native code is necessary but not sufficient for typical system programming language tasks, like hardware drivers, OS kernels, and language runtimes.

> No, because I have listed Ada in my list.

I have no idea how that would refute my statement.

> Better improve your CS skills.

Don't be so condescending.

> Oberon GC in Oberon

I stand corrected: You're right, the GC runtime for a language with non-optional GC can be written in the language itself. I guess a better, more precise indicator are the following two requirements:

1) Native support for calling OS services, for example through system calls (specifically, reservation of memory space). 2) Pointers, pointer arithmetic, and reading/writing memory through pointers.

I don't see how one could implement the GC runtime without these features. (Note that native support for heap-allocation means that GC is optional anyway). What do you think?


> I have no idea how that would refute my statement

Ada is used for building systems running on bare metal.

> Native support for calling OS services, for example through system calls (specifically, reservation of memory space). 2) Pointers, pointer arithmetic, and reading/writing memory through pointers.

Oberon variants and Modula-3 support GC pointers and have been used to write full OSs.

Pointer arithmetic is possible in code regions marked as unsafe.

The Oberon based ones were used for several years at Swiss Federal Institute of Technology.

Spin (Modula-3) didn't had such luck, as DEC and Olivetti got closed down.

Just read the code provided in the link for Oberon's example.

The problem is that many think GC == Java, while during the years Java was getting loose into the world, there where languages like Cedar, Oberon and Modula-3 that while having a GC do offer the fine grain control one needs for system programming.

> Note that native support for heap-allocation means that GC is optional anyway

Not really. In safe systems programming language the GC is always there and you need to mark the code as unsafe when allocating from foreign heap. Meaning you are responsible for not tainting the whole code base.

For example, in the Oberon OS only the Kernel and driver modules do manual allocation.

Actually, Java 9 is getting the missing features from safe systems programming languages (some of them will only come in Java 10).


There are operating systems implemented in many of the languages pjmlp listed.


> Not sure what you are trying to imply here?

He's saying that being interpreted and being high level are orthogonal.

> Well, that would negate a lot of benefits of using some of the higher level languages and/or their portability of architecture claims.

Portability and high level-ness are also orthogonal.


> Well, most higher level languages are getting interpreted, which require some cpu time to startup. Even with something not-so-high-level-but-sorta-high-level like Java or C#, you still have an abstraction level (JVM/CLR) that must first boot, then interpret, then execute.

Startup times of typical VMs on modern hardware are mostly insignificant compared to initializations of various libraries and frameworks inside the actual application (and in many GUI applications there is outright call to sleep() in initialization code).

On the other hand this seems somewhat inconsistent with the fourth point. For example GIMP plugins are external processes and most of the (long) startup time of GIMP is spent exec'ing random small binaries that output few bytes of data and then exit.


> Something is wrong if most programs don't run instantaneously

Agreed. BBC BASIC V on my Archimedes had zero startup penalties, unlike modern JIT languages.

> Highly optimizing compilers aren't worth the risk

Agreed. The problem here is unforseen overflow resulting from machine representation of ints.

> Design applications as small executables that communicate

Agreed. Both UNIX and Erlang have demonstrated the efficacy of this approach.

> Don't write temporary files to disk, ever

Agreed. WANG wordprocessors routinely outperformed MS Word 6.0 due to their RAMDISCs.

> Everything is so complex that you need to isolate yourself from as many libraries and APIs as possible

Agreed. BBC BASIC V did everything I needed without need of external graphics libraries.

> C still doesn't have a module system

Agreed. Indeed, do we actually even need the complexity of C++ to remedy C's deficiencies?


A reason not to have small communicating binaries is that communication adds overhead for serialization and deserialization.


Performance overhead and programming/complexity overhead, because you basically have to treat incoming data as external input which has to be thoroughly validated (more thoroughly than data structures coming from within the same program).




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: