Live data from Hacker News

An incoherent Rust

boxyuwu.blog

41–50 of 175 posts

Re: An incoherent Rust

#41

Earlier quoted context omitted.

can you elaborate on how have they contributed to the quality of the ecosystem?

there is no good way to handle colliding implementations. Both from parallel crates and due to changes over time. Without it you can have many many additional forms of breakage. Worse you can have "new" breakage between two 3rd party crates without either of them changing due to some impl in a common ancestor changing (e.g. std) and this affecting two wild card implementations in each, now leading to an overlap. When…

> In many other ecosystems it's not uncommon to run into having issues where certain libraries can't be used together at all.

The same problem exists in Rust, but from the other side.

If I use serde for serialization I am effectively locked in to using crates that implement serde traits (or do newtype hacks to define them myself).

If I want to use something more niche than serde, I essentially lose access to all the popular crates as they only implement serde traits.

Re: An incoherent Rust

#42
Similar but not exactly the same as named impls, I'd really like to see a language handle this by separating implementing a trait from making a particular existing implementation the implicit default. Orphan rules can apply to the latter, but can be overriden in a local scope by any choice of implementation.

This is largely based on a paper I read a long time ago on how one might build a typeclass/trait system on top of an ML-style module system. But, I suspect such a setup can be beneficial even without the full module system.

Re: An incoherent Rust

#43
post #29

I’m not convinced that the problem is actually a problem. Suppose someone writes a type PairOfNumbers with a couple fields. The author did not define a serialization. You use it in another type and want it to serialize it as: { "a": 1, "b": 2 } I use it and want to serialize it as: [ 1, 2 ] What we’re doing is fine. You should get your serialization and I should get mine. But if either of us declares, process-wide, t…

> What we’re doing is fine. You should get your serialization and I should get mine. But if either of us declares, process-wide, that one of us has determined the One True Serialization of PairOfInts, I think we are wrong.

Well, fine, but then you need to actually implement a module system or something. Currently trait impls are program-wide, and if you say that you're not allowed to make global impls of a trait then that's the same as saying you're not allowed to implement traits at all.

Re: An incoherent Rust

#44
post #15

This is one of the (several?) things that make me very worried about Rust long-term. I love the language, and reach for it even when it sometimes isn't the most appropriate thing. But reading some of the made-up syntax in the "Removing Coherence" section makes my head hurt. When I used to write Scala, I accepted the fact that I don't have a background in type/set/etc. theory, and that there were some facets of the la…

I really wouldn't worry much. Over the last decade of rust, very few of the articles exploring new syntax have turned into anything controversial by the time they were merged (I can't even think of big rust syntax changes other than `impl T` in arguments etc). The singular example really is async/await and, having been quite worried about it / against it at the time, it was really nothing to be concerned with at all.

Re: An incoherent Rust

#45
post #15

This is one of the (several?) things that make me very worried about Rust long-term. I love the language, and reach for it even when it sometimes isn't the most appropriate thing. But reading some of the made-up syntax in the "Removing Coherence" section makes my head hurt. When I used to write Scala, I accepted the fact that I don't have a background in type/set/etc. theory, and that there were some facets of the la…

> I wonder if C++ has some hairy concepts and syntax today

Both better and worse.

The current version of idiomatic C++ is much cleaner, more concise, and more powerful than the version of C++ you are familiar with. You don't need C-style macros anymore. The insane template metaprogramming hacks are gone. Some important things that were problematic to express in C++ (and other systems languages to be fair) are now fully defined e.g. std::launder. C++ now has expansive compile-time programming features, which is killer for high-performance systems code, and is more expressive than Rust in important ways.

The bad news is that this was all piled on top of and in addition to the famous legacy C++ mess for backward compatibility. If you are mixing and matching ancient C++ with modern C++, you are going to have a bad time. That's the worst of all worlds.

But if you are lucky enough to work with e.g. an idiomatic C++20 code base, it is a much simpler and better language than legacy C++. I've done a few major C++ version upgrades to code bases over the years; the refactored code base was always smaller, cleaner, safer, and easier to maintain than the old version.

Re: An incoherent Rust

#46
post #15

This is one of the (several?) things that make me very worried about Rust long-term. I love the language, and reach for it even when it sometimes isn't the most appropriate thing. But reading some of the made-up syntax in the "Removing Coherence" section makes my head hurt. When I used to write Scala, I accepted the fact that I don't have a background in type/set/etc. theory, and that there were some facets of the la…

Reflection syntax (C++26 I think) has made my 30+ years-of-C++ brain melt. It's not insane, it's just ... melt-inducing.

Yeah, for me reflection and coroutines were the first changes to C++ where the implementation and use mechanics weren't immediately obvious by reading a few references. It requires a bit of proper study to wrap your head around it.

Re: An incoherent Rust

#47
post #21

I don't think explicit naming of impls is wise. They will regularly be TraitImpl or similar and add no real value. If you want to distinguish traits, perhaps force them to be within separate modules and use mod_a::mod_b:: syntax. > An interesting outcome of removing coherence and having trait bound parameters is that there becomes a meaningful difference between having a trait bound on an impl or on a struct: This se…

I don't think fully-qualified paths are enough on their own. You also need some way to designate that an impl is symbolically unique and has to be referred to by path. Otherwise, you still end up with a problem where the compiler doesn't know which implementation to use unless you precisely name it. You depend on crates A and B. A impls Foo for Bar. You pass an instance of Bar to a function that accepts `impl Foo`. Y…

> You depend on crates A and B. A impls Foo for Bar. You pass an instance of Bar to a function that accepts `impl Foo`. You are happy. Later crate B adds an impl of Foo for Bar. Clearly _at least_ one of these must be an orphan impl, but both could be. Suddenly it's ambiguous which implementation of Foo you're talking about, so you break because B added an impl.

I think that's fine. Same as what happens if B adds a new function with the same name as a function in A that you were using unqualified.

> What happens, for example, if an impl that was an orphan impl in one version of A becomes a coherent impl in a later version of A?

Nothing much?

Re: An incoherent Rust

#48

Earlier quoted context omitted.

Rust opened the door to innovation in the low-level languages space, but as long as it is already the most theoretically advanced practical language there, it will always attract the audience that actually wants to push it further. I don't know if there is a way to satisfy both audiences.

I think there is: a schism. Another language, inspired, intelligible and interoperable with Rust, but with other goals, likely ease of use or surface simplicity. In my mind it would be pretty much the same as Rust, but whenever a compile error gives you a suggestion in rustc would instead compile (and at most be a warning in this hypothetical language). Migrating from Rust to this language would be changing a single…

I mean… Sure, if we’re just making stuff up, a compiler that can magically understand whatever you were trying to do and then do that instead of what you wrote, I guess that’s a nice fantasy?

But out here on this miserable old Earth I happen to think that Rust’s errors are pretty great. They’re usually catching things I didn’t actually intend to do, rather than preventing me from doing those things.

Re: An incoherent Rust

#49
post #15

This is one of the (several?) things that make me very worried about Rust long-term. I love the language, and reach for it even when it sometimes isn't the most appropriate thing. But reading some of the made-up syntax in the "Removing Coherence" section makes my head hurt. When I used to write Scala, I accepted the fact that I don't have a background in type/set/etc. theory, and that there were some facets of the la…

> I wonder if C++ has some hairy concepts and syntax today on par with Rust's more difficult parts. … … … … Unqualified name lookup has been challenging in C++ since even before C++11. Overload resolution rules are so painful that it took me weeks to review a patch simply because I had to back out of trying to make sense of the rules in the standard. There's several slightly different definitions of initialization. I…

> starting playing around with std::launder and std::byte and strict aliasing rules and lifetime rules, and you'll yearn for the simplicity of Rust

Annotations like std::launder, lifetime manipulation, etc solve a class of problems that exist in every systems language. They inform the compiler of properties that cannot be known by analyzing the code. Rust isn't special in this regard, it has the same issues.

Without these features, we either relied on unofficial compiler-specific behavior or used unnecessarily conservative code that was safe but slower.

Re: An incoherent Rust

#50
post #12

Earlier quoted context omitted.

This is also where I find it surprising that this article doesn't mention Scala at all. There are MANY UX/DX challenges with the implicit and witness system in Scala, so I would never guess suggest it directly, but never have I felt more enabled to solve my own problems in a language (and yes the absolute most complex, Haskell-in-Scala libraries can absolutely an impediment to this). With AI this pace difference is e…

The problem is existentials, or rather the existence of existentials without the ability to explicitly override them. Even in Haskell, overriding typeclass instances requires turning off orphan checks, which is a rather large hammer. So once you've identified this, now you might consider the universe of possible solutions to the problem. One of those solutions might be removing existentials from your language; think…

Yep, familiar with all of this.

That said, I would love to see a solution in my favorite class of solution: where library authors can use and benefit from this, but the average user doesn't have to notice.

I tend to think that the non-existential Scala system was _so close_, and that if you _slightly_ tweaked the scoping rules around it, you could have something great.

For example, if - as a user - I could use `.serialize(...)` from some library and it used _their_ scoped traits by default, but if I _explicitly_ (named) imported some trait(s) on my side, I could substitute my own, that'd work great.

You'd likely want to pair it with some way of e.g. allowing a per-crate prelude of explicit imports that you can ::* import within the crate to override many things at once, but... I think that with the right tweaks, you could say 'this library uses serde by default, but I can provide my own Serializer trait instead... and perhaps, if I turn off the serde Cargo feature, even their default scoped trait disappears'.

Post reply on HN