5 ms·
Why is the author talking about null in the intro, which implied using pointers and thus boxed objects and then running benchmarks on integers? That makes no se
by Yujf 4y ago
Why is the author talking about null in the intro, which implied using pointers and thus boxed objects and then running benchmarks on integers? That makes no sense to me.
- sirwhinesalot 4y agoBecause it's a benchmark on Optionals? In Java an Optional<Long> requires boxing, in Rust it does not. You'd expect a "sufficiently smart compiler" to detect this and avoid needless boxing after inlining and escape analysis but clearly that is not the case. Note that "Long" in Java can be null because it is boxed, "long" (lowercase) however cannot be null, but it also can't be Optional<long>. Java sucks :) EDIT: I'd love to see a C# version of this.
- winrid 4y agoJava's language philosophy is simple - everything must be an object. This simplifies a lot of things. Rust has optionals built into the language. Rust's philosophy is to be a super powerful tool, language complexity be damned. I find it hard to say java sucks in this context. Each language is making trade offs that align with their vision.
- thaumasiotes 4y ago> Java's language philosophy is simple - everything must be an object. This simplifies a lot of things. Everything except primitive types, functions, and arrays (of any type). The different status of arrays can be a real pain. Ruby says the same thing, and they're even worse about functions behaving differently than objects do.
- pharmakom 4y agoThe Java compiler “sucks” (not the language) because it’s not making optimisations that are safe and a human could do themselves fairly simply.
- sshine 4y agoThe lack of optimisation here isn’t a dealbreaker. The fact that Optional<T> can also be null, because it’s a reference type, makes it a less safe implementation of optionals. That’s why the newer standard library uses static methods in a lot of places, e.g.: Optional.ofNullable(theField)
- masklinn 4y agoThat’s just a convenience for theField == null ? Optional.empty() : Optional.of(theField); As Optional.of will raise an NPE if given a null value. It doesn’t change the issue that the Optional itself can be null, and that null is its default value.
- TazeTSchnitzel 4y agoRust doesn't have optionals built-in. The language has no special support for them (beyond the try operator); just like Java, Rust's optional type is provided by the standard library, but it could be trivially implemented yourself and your implementation would have the same behaviour and performance characteristics. It's literally just: enum Option<T> { Some(T), None, } What makes Rust fast here is that it has value types and can optimise them.
- tialaramex 4y agoEven for the try operator (?) it is these days just an unstable Trait that you are welcome to implement for your own types. Once that's stabilized it's not any different from how types can implement PartialEq and get == and !=. The extra edge beyond not needing boxing is from niches, if T doesn't occupy all possible bit representations Rust will squeeze None into one of the unused bit values. Several standard library types have such niches, but today there is no stable way to make your own yet.
- masklinn 4y ago> Several standard library types have such niches, but today there is no stable way to make your own yet. However note that if a type has suitable niches enums will automatically take advantage of those. So that’s more of a concern with T than with a bespoke Option, that’ll work OOTB.
- tialaramex 4y agoYes, Rust's Guaranteed Niche Optimisation says if you make this: enum Foo<T> { Nasty, Nice(T), } ... and T has a niche, it is guaranteed that Foo<T> has the same size as T and Nasty just slots into the niche. In practice, other fancier things will get optimised, but Rust doesn't guarantee exactly what will or will not be handled, e.g. if the niche has room for four things, and I make an enum with four extra plain variants, the guarantee doesn't apply but probably that'll work.
- atq2119 4y ago
- u320 4y ago> Rust's philosophy is to be a super powerful tool, language complexity be damned. This is not Rust's philosophy at all.
- sshine 4y ago> Note that "Long" in Java can be null because it is boxed, "long" (lowercase) however cannot be null, but it also can't be Optional<long>. Java sucks :) I think using primitive types as generics is something that makes Java less ergonomic than C# (where they’re called unmanaged types), whether it is considered justified or necessary. To say Java sucks because of this is a bit much. To say Java sucks because you can’t avoid null is definitely warranted. (You can say good things about Java, and not being able to opt out of nulls is not one of them.)
- jonhohle 4y agoBut `Optional` could have been a value type from the start and had effectively zero overhead, especially if it were specialized for primitive types. There are 8 primitive types, so supporting them all with a value-type optional would not have been the end of the world, even if it was only a language level optimization (e.g. optional becomes a 96-bit-128-bit type and the compiler is responsible for ensuring primitives are wrapped/unwrapped specially). GNU Trove is a collection library that focuses on optimizing for primitive types and is significantly faster that Java collections which require boxing.
- winrid 4y agoNever heard of Trove, thanks! Have only used fastutil, which looks to be about the same thing.
- kaba0 4y agoThere is OptionalLong in the standard library, though. Java just can’t do generic specialization as of yet, which is necessary for Optional<long> to be a different implementation (+ value types of course)
- jonhohle 4y agoBut `OptionalLong` is a heap allocated object. I’m suggesting a wider primitive that can be passed on the stack (value plus flags to indicate state) bypassing any need for allocation. In Java this can only be provided by the compiler (without resorting to some programming and value convention).
- kaba0 4y agoWell, Java doesn’t specify that it has to be heap allocated and it will in fact not allocate that object if the producer function can be inlined and the escape analysis deems so (which happens surprisingly often). But here is another option if you really want to avoid that allocation (besides of course using ByteBuffer and similar which is always a possibility): https://news.ycombinator.com/item?id=35133577 https://news.ycombinator.com/item?id=35133577
- tpm 4y ago> Java's language philosophy is simple - everything must be an object. Except primitive types like long in this case, which are not objects. This was a performance-consistency tradeoff made in the early 90s. It made sense at the time and now doesn't make sense to some people, but that's ok. I wouldn't say Java sucks because of that either. Now type erasure, that's a different topic.
- winrid 4y agoYeah the type erasure is pretty bad. Can be useful though :)
- komadori 4y agoRust doesn't have optionals built into the language except insofar as Option<T> is defined in the standard library. The difference is that Rust allows you to define new value-types, whereas Java has a small fixed set of "primitive" value-types.
- Inityx 4y agoAlso from the beginning of the article: > The task was to compute a sum of all the numbers, skipping the number whenever it is equal to a magic constant. The variants differ by the way how skipping is realized: > 1. We return primitive longs and check if we need to skip by performing a comparison with the magic value directly in the summing loop. > 2. We return boxed Longs and we return null whenever we need to skip a number. > 3. We return boxed Longs wrapped in Optional and we return Optional.empty() whenever we need to skip a number. Seems pretty reasonable to me.
- alkonaut 4y agoAnd the only one that truly would make sense would of course be Optional<long>, i.e. the optional primitive long... First having to declare the value in the one type of four that makes least sense, then praying that the compiler optimizes the allocation of not one but TWO(!) objects(!) in order to represent "maybe a number" is basically why I ragequit Java almost 20 years ago.
- kaba0 4y ago20 years ago there were no generics, so you couldn’t have implemented it that way. You could have written a class OptionalLong { long value; boolean isSet; } at the time and that would have only a single allocation overhead. Alternatively, have an array of longs and a boolean array marking which ones are set, with a trivial wrapper object over that for essentially zero overhead. Java’s tradeoffs are maintainability in huge teams over multiple years with relatively fast performance even if you write your code very naively, with top notch tooling, observability, etc. In the rare case you have to optimize in the hot loops you can allow to have less readable code like I mentioned.
- alkonaut 4y ago> 20 years ago there were no generics, so you couldn’t have implemented it that way. You could have written a class OptionalLong That was the actual reason, yeah. Basically having to make IntList and so on. > Java’s tradeoffs are maintainability in huge teams over multiple years with relatively fast performance When I did switch to C# in 2003, it was very young. Since then generics have been bolted on and so on, but I didn't find any hit to maintainability due to this. What I do think was sad though is that when Generics were bolted on (and value types obviously were there all along), that the APIs didn't immediately include some easy and obvious wins like Option<T>. Those have been reimplemented since ad nauseam.