Nothing justifies the prolonging of C torture either, except of the C's wide spread. Why do you think modern CPUs still expose mostly C-abstract-machine-like interface instead of their actual out-of-order, pipelined, heterogeneous-memory-hierarch-ied internal workings?
>> Why do you think modern CPUs still expose mostly C-abstract-machine-like interface instead of their actual out-of-order, pipelined, heterogeneous-memory-hierarch-ied internal workings?
Because exposing that would be a huge burden on the compiler writers. Intel tried to move in that direction with Itanium. It's bad enough with every new CPU having a few new instructions and different times, the compiler guys would revolt if they had to care how many virtual registers existed and all the other stuff down there.
But why C? If you want languages to interface with each other it always comes down C as a lowest common denominator. It's even hard to call C++ libraries from a lot of things. Until a new standard down at that level comes into widespread use hardware will be designed to run C code efficiently.
> Until a new standard down at that level comes into widespread use hardware will be designed to run C code efficiently.
Exactly this hinders any substantial progress in computer architecture for at least 40 years now.
Any hardware today needs to simulate a PDP-7 more or less… As otherwise the hardware is doomed to be considered "slow" should it not match the C abstract machine (which is mostly a PDP-7) close enough. As there is no alternative hardware available nobody invests in alternative software runtime models. Which makes investing in alternative hardware models again unattractive as no current software could profit from it. Here we're gone full circle.
It's a trap. Especially given that improvements in sequential computing speed are already difficult to achieve and it's known that this will become even more and more difficult in the future, but the computing model of C is inherently sequential and it's quite problematic to make proper use of increasingly more parallel machines.
What we would need to overcome this would be a computer that is build again like the last time many years ago, as a unit of hard and software which is developed hand in hand with each other form the ground up. Maybe this way we could finally overcome the "eternal PDP-7" and move on to some more modern computer architectures (embracing parallelisms in the model from the ground up, for example).
I don't have words to describe how exciting that would be. The only way I could see it happen is if the existing legacy architecture would be one (or many) of the parallel processes so that efforts to make it run legacy software don't consume the entire project. I really do think it possible to make a "sane" machine language that doesn't need layers of abstraction or compilers and is easy to learn.
It is not the same C, it is a dialect full of extensions, and it only applies to OpenCL, which it was one of the reasons why it failed and forced Khronos to come up with SPIR, playing catch up the polyglot PTX environmnent of CUDA.
OpenCL 3.0 is basically OpenCL 1.2, which is OpenCL before SPIR was introduced.
>> Especially given that improvements in sequential computing speed are already difficult to achieve and it's known that this will become even more and more difficult in the future...
That's perfect. As performance stop increasing just by shrinking transistors, other options will have a chance to prove themselves.
>> but the computing model of C is inherently sequential and it's quite problematic to make proper use of increasingly more parallel machines.
IMHO Rust will help with that. The code analysis and ownership guarantees should allow the compiler to to decide when things can be run in parallel. Rust also forces you to write code that will be easier to do that. It's not a magic bullet but I think it will raise the bar on what we can expect.
> If you want languages to interface with each other it always comes down C as a lowest common denominator
Nope, "always" only applies to OS written in C and usually following POSIX interfaces as they OS ABI.
C isn't the lowest common denominator on Android (JNI is), on Web or ChromeOS (Assembly / JS are), on IBM and Unisys mainframes (language environments are), on Fuchsia (FIDL is), just as a couple of examples.
CPUs expose a "mostly-C-abstract-machine-like" interface because this allows chip designers to change the internal workings of the processor to improve performance while maintaining compatibility with all of the existing software.
It has nothing to do with C, specifically, but with the fact that vast amounts of important software tend to be distributed in binary form. In a hypothetical world where everybody is using Gentoo, the tradeoffs would be different and CPUs would most likely expose many more micro-architectural details.
> Why do you think modern CPUs still expose mostly C-abstract-machine-like interface
I don’t think that, because they don’t. Your premise is hogwash.
Modern RISC derived CPUs for the most part expose a load store architecture driven by historical evolution of that micro arch style and if they are SMP a memory model that only recently has C and C++ adapted to with standards. Intels ISA most assuredly was not influenced by C. SIMD isn’t reminiscent of anything standard C either.
Also you might want to look into VLIW and the history of Itanium for an answer to your other question.
There is one CPU that exposes its out of order inner workings, the VIA C3 ("ALTINST"). The unintended effects are so bad that people that accidentally discovered it referred to it as a backdoor: https://en.wikipedia.org/wiki/Alternate_Instruction_Set
> "In 2018 Christopher Domas discovered that some Samuel 2 processors came with the Alternate Instruction Set enabled by default and that by executing AIS instructions from user space, it was possible to gain privilege escalation from Ring 3 to Ring 0.["
The "sufficiently smart compiler" [1] has been tried often enough, with poor enough results, that it's not something anyone counts on anymore.
In this case, the most relevant example is probably the failure of the Itanium. Searching for that can be enlightening too, but heres a good start: https://stackoverflow.com/questions/1011760/what-are-the-tec... (For context, the essential Itanium idea was to move complexity out of the chip and into the compiler.)
Also, don't overestimate Haskell's performance. As much fun as I've had with it, I've always been a bit disappointed with its performance. Though for good reasons, it too was designed in the hopes that a Sufficiently Smart Compiler would be able to turn it into something blazingly fast, but it hasn't succeeded any more than anything else. Writing high-performance Haskell is a lot like writing high performance Javascript for a particular JIT... it can be done, but you have to know huge amounts of stuff about how the compiler/JIT will optimize things and have to write in a very particular subset of the language that is much less powerful and convenient than the full language, with little to no compiler assistance, and with even slight mistakes able to trash the perfromance hardcore as some small little thing recursively destroys all the optimizations. It's such a project it's essentially writing in a different language that just happens to integrate nicely with the host.
This is reminiscent of my experience designing SQL queries to be run on large MySQL databases.
I had to write my queries in such a way that I was basically specifying the execution plan, even though in theory SQL is practically pure set theory and I shouldn’t have to care about that.
Well it turned out that for running scalar code with branches and stack frames exposing too much to the compiler was not helpful, especially as transistor budgets increased. So as long as we program with usual functions and conditionals this is what we have.
I can swap out a cpu for one with better IPC and hardware scheduling in 10 minutes but re-installing binaries, runtime libraries, drivers, firmware to get newly optimized code -- no way. GPU drivers do this a bit and it's no fun.
For a long time I thought the JS hate was just a friendly pop jab. From working with backend folks I’ve realized it comes from at least a somewhat patronizing view that JS should feel more like backend languages; except it’s power, and real dev audience, is in browsers, where it was shaped and tortured by the browser wars, not to mention it was created in almost as many days as Genesis says the world was built.
Huh? Python and powershell are all over the backend, and it’s hard to argue against JavaScript while you’re using those. At least in my opinion.
I think it has more to do with the amount of people who are bad at JavaScript but are still forced to sometimes work with it because it’s the most unavoidable programming language. But who knows, people tend to complain about everything.
Sad to see the word "objective" become the next bullshit intensifier because people can't separate their own subjective opinions from the realm of verifiable fact.
You can compare Javascript to other languages and note that many of its notorious problems have no rational justification, and are unnecessary. That's what I call objectively terrible.
The comparative approach I mentioned can be used to eliminate personal feelings about such issues - not in all cases (types might be an example), but certainly in some.
Many users of Javascript, including myself, recognize that it has many weaknesses. There's even a book that acknowledges this in its title: "Javascript: The Good Parts."
Denying this seems to be denying objective reality.
You may be confusing "objective" with "universal," thinking that I'm claiming some unsituated universal truth. But that's not the case. Any statement is only ever true within some context - the language that defines it, the semantics of the statement, the premises that it assumes.
In this case, there is a shared context that crosses programming languages, that allows us in at least some cases to draw objective conclusion about programming language features. "The bad parts" implied by Crockford's title includes many such features. We can examine them and conclude that while they might have some historical rationale, that they are not good features for a programming language to have.
In many cases this conclusion is possible because there's simply no good justification - the title of this post is an example. Having all numbers be floating point has many negative consequences and no significant positive ones - the only reason for it is historical. Such features end up having consequences, such as on the design of hardware like ARM chips. That is an objectively terrible outcome.
You can of course quibble with such a statement, based on a rigid application of a simplistic set of definitions. But you'd do better to try to understand the truth conveyed by such a statement.
You sound like a complete novice, funboy or the one who knows only JS. There are many issues, it is possible not to touch or to work around them, TS and flow helps. JS solved just a few — 'use strict', strict comparison, arrow functions, string literals. Core problems still there — implicit types, prototype declarations, number is float, toString/inspect division, typeof null. Every javascript programmer has to walk that way of embarrassment.
JavaScript has good parts, I write it a lot. But it is ignorant to close eyes on its warts
1 + '2'
1 - '2'
Number.MAX_SAFE_INTEGER + 2
and entire WAT series, stems from "don't raise" ethos. JavaScript exposes constructor instead of prototype that messed up a lot, in Ruby terms
Object.alias_method :__proto__, :class
Object = Object.instance_method(:initialize)
Class = Class.instance_method(:initialize)
Class.__proto__.alias_method :prototype, :owner
new = ->(constructor) { constructor.owner.new }
Person = Class.prototype.new do
def initialize
end
end.instance_method(:initialize)
def Person.foo
'foo'
end
puts Person.foo
john = new.call Person
def john.bar
'bar'
end
puts john.bar
def (Person.prototype).baz
'baz'
end
puts john.__proto__.baz
Does anyone wants to adopt this feature in their language?
Operator overloading can lead to ambiguities in dynamic languages. Ruby, python, and any number of other languages have it much worse because they can be overloaded any way you want while JS overloads are (currently at least) set in stone by the language.
If you could only choose one number type, would it be floats or ints? Crockford would say decimal, but the rest of use using commodity hardware would choose floats every time. It's not the language implement's fault someone doesn't understand IEEE 854. This max safe integer issue exists in ALL languages that use IEEE 854. In any case, BigInt is already in browsers and will be added to the spec shortly.
1n - "2"
1 + 1n
Uncaught TypeError: Cannot mix BigInt and other types, use explicit conversions
works nice with string interpolation:
`${1n}`
"1"
Numbers is a sane example. One can argue it was for good. How about `{} + []`? I believe I can disable this part in JavaScript engine and no one would notice. And misleading `object[key]` where it calls toString, sure I have not tried that in a decade but it is stupid. UTF-16:
""[1] # there were emoji
//"�"
You've said nothing about constructor oriented programming. Unique feature, I have not heard any other language adopted it yet. The post you've replied contents sketch for Ruby. Actually I've got it wrong — every JavaScript function is a closure (Ruby method is not closure) and Prototype method was a class method (not instance method), fixed but ugly:
def function(&block)
Class.prototype.new.tap do |c|
c.define_method(:initialize, block)
end.instance_method(:initialize)
end
def function_(object, name, &block)
object.class_eval do
define_method(name, &block)
end
end
Person = function { |name|
@name = name
}
function_(Person.prototype, :name_) {
@name
}
john = new.call Person, 'john'
puts john.__proto__ == Person.prototype
puts john.name_
def function__(object, name, &block)
object.singleton_class.class_eval do
define_method(name, &block)
end
end
function__(john, :name__) {
@name
}
puts john.name__
By the way, you can say "Yes, I know JavaScript has some problems". It is not a secret, everyone knows.
Sure, until you parachute into a code base where several generations of contractors added features that communicate over a shared global object. This is bad per-se, but becomes worse when your language allows one to add fields on the fly and you end up with this container full of similar fields because eventually nobody knows exactly what’s in the object any more...
And that seems pretty simple to fix. "The same level of awareness that created a problem cannot be used to fix the problem" - and maybe that's why they hired you, to fix these problems. I've been that guy before. What was really fun was the codebase I had to fix that used mexican slang words as variable names, and I only speak English. So much fun. But I sucked it up, accepted the challenge, and I improved it.
It really doesn't take a super-genius to code javascript simply, efficiently, and without errors. Funny that a lot of programmers that think they're very smart are the ones that either shit on javascript, or make a lot of stupid errors with it, or both.
I've seen similar abuses with global maps in other languages (essentially the same). This is an architecture fault rather than a language fault.
As you say, that is a problem with any language and project with a revolving door of developers. Perhaps those companies should learn their lesson and hire at least one or two good, permanent senior devs to keep things on track.
Like always, the human factor outweighs almost everything else.
I'd love to have a way to take a `Date` object and set it as the value of a `datetime-local` input. Sure feels like that should be straightforward to do, without requiring any weird conversions like it does.
I think it goes beyond prescriptivism/descriptivism.
Descriptivism refers generally to describing how a community uses language, so the most common usages end up being the primary definitions. Once documented, these tend to become prescriptive.
In that context, a single author who uses a word in an unusual way would likely not have any impact on the descriptive or prescriptive definitions of that word, unless of course their usage becomes common.
Humpty was arguing for the benefits of using words in unusual ways, which potentially violates both prescriptivism and descriptivism.
Impressionistic use of words is one example of this, where the words used might convey a certain feeling, perhaps via their connotations, even though their literal definitions may not be exactly appropriate. This kind of usage is generally found in literature where language is being used as an artistic tool rather than a prosaic tool of communication.
So you're saying we should applaud reducing it by 1-2% right?
To answer the question of if it's worth it to add this specialized instruction, it really depends on how much die space it adds, but from the look of it, it's specialized handling of an existing operation to match an external spec; that can be not too hard to do and significantly reduce software complexity for tasks that do that operation. As a CE with no real hardware experience, it looks like a clear win to me.
This is like saying “nothing justifies the prolonging of capitalist torture”. On some level it’s correct, but it’s also being upset at something bordering a fundamental law of the universe.
There will always be a “lowest common denominator” platform that reaches 100% of customers.
By definition the lowest common denominator will be limited, inelegant, and suffer from weird compatibility problems.
If it wasn’t JavaScript it would be another language with very similar properties and a similar history of development.