Doing as you suggest creates a tight(er) coupling between your client side language and your database.
That can be okay - there's less code to write and maintain, and in theory debugging could / should go a little quicker.
On the other hand, it makes other aspects more difficult. If / when you need to change either of those technologies, you'll have a harder time because of the tight coupling between them.
Protecting yourself against attacks will be (quite) a bit more difficult. You're assuming that the client will always present nicely formatted requests to the database. That presumes no one will ever hack the client side code to insert malicious statements. In other words, they'll "borrow" your authentication mechanisms and replace the normal client code with theirs.
I wouldn't recommend it, and many would vehemently tell you not to. But it can be done.
Yes, it pretty much comes down to efficiency. But you seem to be underestimating the impact (or overestimating how well various optimizations work).
First, it's not just "spatial overhead". Making primitives boxed/heap-allocated has performance costs too. There's the additional pressure on the GC to allocate and collect those objects. This goes doubly if the "primitive objects" are immutable, as they should be. Then there's more cache misses (both because of the indirection and because less data fits into a given amount of cache). Plus the bare fact that "load the address of an object, then load the actual value from that address" takes more instructions than "load the value directly".
Second, escape analysis isn't go-faster fairy dust. It only applies to values that, well, don't escape. It's certainly nice to optimize local calculations (such as loop counters and intermediate results of calculations) and it will give measurable benefits. But a much larger majority of values live in the fields of objects and arrays. Granted, those can be subject to escape analysis themselves, but as they're usually mutable reference types, any aliasing of them presents a significant challenge to the escape analysis, which now has to prove that those aliases (1) don't escape either, and (2) don't make a difference for the purpose of eliminating allocations.
Given that calling any method (including getters) or passing an object as argument to any other method can help the object escape, you'll need interprocedural analysis in all but the most trivial cases. This is far more expensive and complicated.
And then there are cases where things really do escape and can't reasonably be optimized away. Quite many of them, actually, if you consider how often C programmers go through the trouble of heap-allocating things. When an object containing an int escapes, escape analysis ceases to apply to the int as well. Say goodbye to efficient primitive fields.
This ties into another point: The analyses and optimizations required are seriously complicated and an active area of research. It's debatable whether any language implementation ever achieved the degree of optimization you suggest, and even if so, it's been a rare and herculean effort. Surely standing on the shoulders of these giants is easier than being a giant yourself, but it's still far from trivial. Don't expect competitive performance any time in the first few years, if ever.
That is not to say such languages can't be viable. Clearly they are. Just don't assume it will be line-for-line as fast as languages with dedicated primitives. In other words, don't delude yourself with visions of a sufficiently smart compiler.
Best Answer
Because implementations of the language are likely to use primitive types as value types rather than reference types. That is, when you assign a value to a new variable, rather than changing the variable such that a contains a reference to the same object used in the source expression, it might copy the value of that object into the variable (if you know C#, think about the different between a
struct
and aclass
). Such a value therefore could not sensibly be used as part of a weak referenced storage system, as there is no object to be garbage collected (just values that get copied around) and therefore the weak key removal would never trigger.The use of values rather than references for these common data types is an optimisation that can produce substantial performance improvements, and therefore the developers of ECMAscript would have been careful to avoid adding any features that mean it couldn't be used.
If you need to use such an item, you can simply create a container object (usually called a "box") to keep the value and allow it to be used in contexts where only references are permitted.