When C was invented, it was very close to reality. It isn't anymore, as you point out. (But as another commenter said, assembly language isn't that close to reality either)
Unfortunately hardware guys and software guys didn't really coordinate, and we just hacked shit up on either side of the instruction set interface.
It is kind of ironic that we write stuff mostly in "serial" languages. But the compiler turns into a parallelizable data flow graph. Then that's compiled back to a serial ISA. And then the CPU goes and tries to execute it in parallel.
It would be a lot nicer if we wrote stuff in parallel/dataflow languages, and the CPU could understand that! Some dilletantism with FPGAs made me realize how mismatched CPUs are for a lot of modern problems.
It's kind of like the idea that Java throws away its type information when compiling to byte code, and then the JIT reconstructs the types at runtime. We have these encrusted representations that cause so much complexity in the stack. C is (relatively) great, but it's also unfortunately one of these things.
It's interesting that you mention Java; some ARM systems have 'Jazelle', a system for directly executing Java bytecode. I don't know how widely used it is.
There were high level dataflow programming languages, like Lucid: http://en.wikipedia.org/wiki/Lucid_(programming_language)
And there were academic dataflow computers as well.
I think D 2.0 makes everything immutable by default (single assignment), so it is close to a dataflow language. And that's no surprise, because Walter Bright designed it specifically to make writing a compiler as natural as possible, rather than having to make complicated inferences about serial code.
My point was that we can never make the jump because the stack of hacks we currently have works "good enough" and it is backward compatible. Generally technologies evolve by accretion, and I'm hard pressed to find an example of radical simplification.
I guess power concerns would be the only hope for something simpler.
It's kind if funny that Backus wondered, "Can programming ever be liberated from the von Neumann model?" This question also applies to hardware too.
Is it a fictive analogy or is it actually how the Java VM behaves?
public T getInstance<T>() {
return this.Instances[typeof(T)];
}
Or, even better: public T createInstance<T>() {
return new T();
}
Meanwhile Java requires this: public <T> T getInstance(Class<T> cls) {
return this.instances[cls];
}
The type information above (the generic parameter T) is stripped after compilation, so the Class parameter is needed on the method to get a solid reference to the type we need to work with at run-time. This gets worse when we want to do the second example. public <T> T createInstance(Class<T> cls) {
return cls.newInstance();
}
It gets even more fun when you throw in non-default constructors. Non-reified generics are a giant pain in the butt.If you look at the JVM byte code, you can see this has to be the case: http://en.wikipedia.org/wiki/Java_bytecode
Once you're in byte code, you don't have your static types anymore -- that is, types that are independent of control flow. You have types on individual instructions.
So basically you lost some type information before the JIT even sees it. Now, it doesn't actually matter for speed, as Mike Pall (LuaJIT author) would say. His point is that dynamically typed languages can be just as fast as statically typed languages, because you have more information at runtime.
I don't understand what you mean by this. Machine code itself abstracts away the underlying hierarchical memory structure. Sure, some machine language might have instructions to manipulate the cache, but those are easily invoked from C, using either inline assembly or __builtin functions.
But I would like to see more work along these lines -- pointers appreciated.
http://scholar.google.com/scholar?cluster=170564211168157303...
Async is provided via co-routines and is as cache friendly as C.
I thought C was so widespread that it eventually started to affect how some computer architectures were designed? If so it seems a bit disingenuous to say that it is only dealing with the reality that it was given.
"To a large extent, the answer is: C is that way because reality is that way. C is a low-level language, which means that the way things are done in C is very similar to the way they're done by the computer itself. If you were writing machine code, you'd find that most of the discussion above was just as true as it is in C: strings really are very difficult to handle efficiently (and high-level languages only hide that difficulty, they don't remove it), pointer dereferences are always prone to that kind of problem if you don't either code defensively or avoid making any mistakes, and so on."
Not really, and not quite. A lot of the complexity of C when it comes to handling strings and pointers is the result of not having garbage collection. But it does have malloc()/free(), and that's not really any more fundamental or closer to the machine than a garbage collector. A simple garbage collector isn't really any more complicated than a simple manual heap implementation.
And C's computational model is a vast simplification of "reality." "Reality" is a machine that can do 3-4 instructions and 1-2 loads per clock cycle, with a hierarchical memory structure that has several levels with different sizes and performance characteristics, that can handle requests out of order and uses elaborate protocols for cache coherence on multiprocessor machines. C presents a simple "big array of bytes" memory model that totally abstracts all that complexity. And machines go to great lengths to maintain that fiction.