Hacker Newsnew | past | comments | ask | show | jobs | submit | QuantumNoodle's commentslogin

I'm just lurking in the comments with popcorn, but if what you said is true and the maintainers of X decided it was a lost cause and unfixable, well that is the most informed opinion of them all. Nobody knows better then the maintainers. Sure, the replacement might have feature gaps initially but that is a transient issue.

> Sure, the replacement might have feature gaps initially but that is a transient issue.

It has been 17 years.


X11 was started in 1984 in MIT. That means, when Wayland was first conceived in 2008, there had been 24 years of X development.

I guess Kristian grossly underestimated the effort required to write a full features Display manager.

FWIW, innmy career the times I've had to perform very impactful changes in software, I always start from the current codebase and remove/simplify stuff.

As an example, once I was in a company that had built a huge Ruby monolith which was not scaling at all. It had APIs for everything, including "high frequency trading" in the same codebase server, under a METAL aws instance (that's how they scaled).

What we did initially was simply copy the repo N times (sign up, compliance, risk, trading, etc), spin up an copies of the same server and use a balancer to route APIs to the different boxes.

Then we started removing unused stuff from each of the repository to specialize them. Fiinally we simplified complexity on each separate codebase.

I would have approached X11 codebase similarly.


I studied signal processing in university and my career evolved to not use what I studied. Decades ago, giving an algorithm a sound file and isolating tracks was difficult.

How does your implementation accomplish this? Were you involved or did you use something off the shelf?

Edit: ah, using neural nets, demucs. I wonder if there is pure math approach that can compete?


Input: poop

Output: Optimizing internal output to drive personal growth and streamline biological efficiency. #WellnessJourney #Efficiency #BioHacking


Still no idea how a polymarket is legal with all the insider trading opportunities.

Insider trading is not about fairness, it's about theft. If you insider trade on the stock market then, in a crude simplification, you steal profits from the company you have a fiduciary duty to, or some extension of that. It has nothing to do with a level playing ground, every trading company out there is trying to find information others can't and then trade on it.

What? How do you figure that? If I happen to know that my company is about to report very bad quarterly numbers, so I sell all my stock, then it tanks, I’ve just screwed whoever bought the stock, that in the most cases, will be some random people. The company does not benefit or hurt from stock prices unless they are buying back or issuing more stock.

Because that's what the law says? And the company most definitely does benefit and hurt from a fluctuating stock price, it's one of the key drivers behind financing conditions. What you describe is a simpleton view of the financial market.

That’s how the criminal penalties are framed.

I ran a TrueNAS server that was based on BSD, loved jails. Then TrueNAS started using debian so more application can run on it. Selfishly I like getting more utility from my server so this was a welcomed change. What industry is BSD used in now a days?

firewalls are a common example (many Linux houses have a BSD-based firewall at the edge) but any "single application" setup may run on BSD - Netflix, for example.

The output the agent creates falls into one of these categories:

1. Correct, maintainable changes 2. Correct, not maintable changes 3. Correct diff, maintains expected system interaction 4. Correct diff, breaks system interaction.

In no way are they consistent or deterministic but _always_ convincing they are correct.


> you need to embed your best practices in your agent and keep and eye on it and refine it over time.

Sincere question, how do beginners to the field (interns, juniors) do this when they don't have any best practices yet?


By working with other people, which I can't help but notice is missing from the parent comment.

Unless you want to be a solopreneur (terrible idea while you don't know what you're doing and don't have the means to hire someone that does), look at pretty much any other comment in this thread.


it's the easiest as it's ever been to get started in a foreign code base: start up the agent and ask questions. more or less instant answers, basically zero confabulations nowadays.

...but since it's so easy to deliver stuff without actually knowing anything, learning means putting in the effort to resist temptation and use the agent as a teaching aid instead of an intern savant.


My advice for juniors is that it's too late to get entry level jobs for software engineering, but AI Automation engineering is just starting. Get a Claude code sub and build whatever you can imagine and focus on improving your own coding agent. Automate one more thing every day.

Software eng has always been automating repetitive decision making and processes. Code is just a series of steps computers/systems follow deterministically. Now we are automating the automation.

I don't necessarily disagree with your advice, but goodness, I don't look forward to using any of the low quality software in the next decade. I hope the shareholders remain happy.


>improving your own coding agent

??????????

write a thousand md files with detailed prompts (and called them skills)?

is that what would get juniors hired? and paid real money? a stack of md files?


"kids" these days don't know what a file is. Seriously, ask anyone you know that is a college professor (in the states).


At a former company, we had an 18yo intern: One day he was given the task to clean-up an old wall closet that wasnt touched for years.

He found some 3.5" floppy disk - he went over to us saying: "why did you 3d-print the Save-Icon from Office so many times"

:-D


Wait, is a "file' a folder or an app?

Yes.

I really don't know why you are being downvoted.

My experience is you are indeed correct. Modern phone/tablet users are so divorced from a file systems that those brought up on them have no idea what a file is or where their data is saved... The data (video, music, shopping-list) simply exists "in the app".

(And don't get me started on their concept of data portability!)


Yep our newest team member came in having never used a posix filesystem in a production environment. It genuinely blew my mind.

...I mean, we've spent the last decade building products with the explicit intention of hiding or otherwise papering over the File abstraction. The hell did anyone expect to happen as a result? People coming in or aging out lose the knowledge as part of context degradation. There was value in keeping it as a first order abstraction.

Cloudflare has been trying to mediate publishers & AI companies. If publishers are behind Cloudflare and Cloudflare's bot detection stops scrapers at the request of publishers, the publishers can allow their data to be scraped (via this end point) for a price. It creates market scarcity. I don't believe the target audience is you and me. Unless you own a very popular blog that AI companies would pay you for.

Next step will be their default "free" anti-bot denying all but their own bot. They know full well nearly nobody changes the default.

I write production Rust code that becomes critical infra for our customers. I got tired of nil checks in Go and became a squeaky wheel in incident retros, where I finally got the chance to rewrite parts of our system in Rust during a refactor.

I admit the skill issue on my part, but I genuinely struggled to follow the concepts in this article. Working alongside peers who push Rust's bleeding edge, I dread reviewing their code and especially inheriting "legacy" implementations. It's like having a conversation with someone who expresses simple thoughts with ornate vocabulary. Reasoning about code written this way makes me experience profound fatigue and possess an overwhelming desire to return to my domicile; Or simply put, I get tired and want to go home.

Rust's safety guardrails are valuable until the language becomes so complex that reading and reasoning about _business_ logic gets harder, not easier. It reminds me of the kid in "A Christmas Story" bundled so heavily in winter gear he cant put his arms down[0]. At some point, over-engineered safety becomes its own kind of risk even though it is technically safer in some regards. Sometimes you need to just implement a dang state machine and stop throwing complexity at poorly thought-through solutions. End old-man rant.

[0]: https://youtu.be/PKxsOlzuH0k?si=-88dxtyegTxIvOYI


I feel you, but hear me out. OP is right. I've wanted pretty much everything he's talking about here for years, I just never thought of all of this in as quite a formal way as he has. We need the ability to say "this piece of code can't panic". It's super important in the domains I work in. We also need the ability to say "this piece of code can't be non-deterministic". It's also super important in the domains I work in. Having language level support for something like this where I add an extra word to my function def and the compiler guarantees the above would be groundbreaking

IMO rust started at this from the wrong direction. Comparing to something like zig which just cannot panic unless the developer wrote the thing that does the panic, cannot allocate unless the developer wrote the allocation, etc.

Rust instead has all these implicit things that just happen, and now needs ways to specify that in particular cases, it doesn't.


The problem isn't implicit things happening.

He's talking about this problem. Can this code panic?

    foo();
You can't easily answer that in Rust or Zig. In both cases you have to walk the entire call graph of the function (which could be arbitrarily large) and check for panics. It's not feasible to do by hand. The compiler could do it though.

"Panic-free" labels are so difficult to ascribe without being misleading because temporal memory effects can cause panics. Pusher too much onto your stack because the function happened to be preceded by a ton of other stack allocations? Crash. Heap too full and malloc failed? Crash. These things can happen from user input, so labelling a function no_panic just because it doesn't do any unchecked indexing can dangerously mislead readers into thinking code can't crash when it can.

There's plenty of independent interest in properly bounding stack usage because this would open up new use cases in deep embedded and Rust-on-the-GPU. Basically, if you statically exclude unbounded stack use, you don't even need memory protection to implement guard pages (or similar) for your call stack usage, which Rust now requires. But this probably requires work on the LLVM side, not just on Rust itself.

Failable memory allocations are already needed for Rust-on-Linux, so that also has independent interest.


What about doing something that Java does with the throws keyword? Would that make the checking easier?

I think that's exactly what's being asked for here (via the "panic effect" that the article refers to)

although, I think i'd prefer a "doesn't panic" effect just to keep backwards compatibility (allowing functions to panic by default)


Or effect aliases. But given that it's strictly a syntactic transformation it seems like make the wrong default today, fix it in the next edition. (Editions come with tools to update syntax changes)

Something like that, except you probably also want to be able to express things like “whatever the callback I’m passed can throw, I can throw all of that and also FooException”. And correctly handle the cases when the callback can throw FooException itself, and when one of the potential exceptions is dependent on a type parameter, and you see how this becomes a whole thing when done properly. But it’s doable.

> Comparing to something like zig which just cannot panic unless the developer wrote the thing that does the panic

The zig compiler can’t possibly guarantee this without knowing which parts of the code were written by you and which by other people (which is impossible).

So really it’s not “the developer” wrote the thing that does the panic, it’s “some developer” wrote it. And how is that different from rust?


Huh? It seems to me that in these respects the two languages are almost identical. If I tell the program to panic, it panics, and if I divide an integer by zero it... panics and either those are both "the developer wrote the thing" or neither is.

In Zig, dividing by 0 does not panic unless you decide that it should or go out of your way to use unsafe primitives [1]. Same for trying to allocate more memory than is available. The general difference is as follows (IMO):

Rust tries to prevent developers from doing bad things, then has to include ways to avoid these checks for cases where it cannot prove that bad things are actually OK. Zig (and many others such as Odin, Jai, etc.) allow anything by default, but surface the fact that issues can occur in its API design. In practice the result is the same, but Rust needs to be much more complex both to do the proving and to allow the developers to ignore its rules.

[1]: https://ziglang.org/documentation/0.15.2/std/#std.math.divEx...


Could you clarify what's going on in the Zig docs[0], then? My reading of them is that Zig definitely allows you to try to divide by 0 in a way the compiler doesn't catch, and this results in a panic at runtime.

I'd be interested if this weren't true, since the only feasible compiler solutions to preventing division-by-0 errors are either: defining the behaviour, which always ends up surprising people later on, or; incredibly cumbersome or underperformant type systems/analyses which ensure that denominators are never 0.

It doesn't look like Zig does either of these.

[0]: https://ziglang.org/documentation/master/#Division-by-Zero


> the only feasible compiler solutions to preventing division-by-0 errors are either: defining the behaviour, which always ends up surprising people later on, or; incredibly cumbersome or underperformant type systems/analyses which ensure that denominators are never 0.

I don't think it's very cumbersome if the compiler checks if the divisor could be zero. Some programming languages (Kotlin, Swift, Rust, Typescript...) already do something similar for possible null pointer access: they require that you add a check "if s == null" before the access. The same can be done for division (and remainder / modulo). In my own programming language, this is what I do: you can not have a division by zero at runtime, because the compiler does not allow it [1]. In my experience, integer division by a variable is not all that common in reality. (And floating point division does not panic, and integer division by a non-zero constant doesn't panic either). If needed, one could use a static function that returns 0 or panics or whatever is best.

[1] https://github.com/thomasmueller/bau-lang/blob/main/README.m...


>Some programming languages (Kotlin, Swift, Rust, Typescript...) already do something similar for possible null pointer access: they require that you add a check "if s == null" before the access.

For Rust, this is not accurate (though I don't know for the other languages). The type system instead simply enforces that pointers are non-null, and no checks are necessary. Such a check appears if the programmer opts in to the nullable pointer type.

The comparison between pointers and integers is not a sensible one, since it's easy to stay in the world of non-null pointers once you start there. There's no equivalent ergonomics for the type of non-zero integers, since you have to forbid many operations that can produce 0 even on non-0 inputs (or onerously check that they never yield 0 at runtime).

>The same can be done for division (and remainder / modulo). In my own programming language, this is what I do: you can not have a division by zero at runtime, because the compiler does not allow it... In my experience, integer division by a variable is not all that common in reality

That's another option, but I hardly find it a real solution, since it involves the programmer inserting a lot of boilerplate to handle a case that might actually never come up in most code, and where a panic would often be totally fine.

Coming back to the actual article, this is where an effect system would be quite useful: programmers who actually want to have their code be panic-free, and who therefore want or need to insert these checks, can mark their code as lacking the panic effect. But I think it's fine for division to be exposed as a panicking operation by default, since it's expected and not so annoying to use.


The syntax in Kotin is: "val name: String? = getName(); if (name != null) { println(name.length) // safe: compiler knows it's not null }" So, there is no explicit type conversion needed.

I'm arguing for integer / and %, there is no need for an explicit "non-zero integer" type: the divisor is just an integer, and the compiler need to have a prove that the value is not zero. For places where panic is fine, there could be a method that explicitly panics in case of zero.

I agree an annotation / effect system would be useful, where you can mark sections of the code "panic-free" or "safe" in some sense. But "safe" has many flavors: array-out-of-bounds, division by zero, stack overflow, out-of-memory, endless loop. Ada SPARK allows to prove absence of runtime errors using "pragma annotate". Also Dafny, Lean have similar features (in Lean you can give a prove).

> I think it's fine for division to be exposed as a panicking operation by default

That might be true. I think division (by non-constants) is not very common, but it would be good to analyze this in more detail, maybe by analyzing a large codebase... Division by zero does cause issues sometimes, and so the question is, how much of a problem is it if you disallow unchecked division, versus the problems if you don't check.


More specifically, Zig will return an error type from the division and if this isn't handled THEN it will panic, kind of like an exception except it can be handled with proper pattern matching.

I can't find anything related to division returning an error type. Looking at std.math.divExact, rem, mod, add, sub, etc. it looks to me like you're expected to use these if you don't want to panic.

Actually you're right, I was going by the source code which was in the link of the comment you replied to, but I missed that that was specifically for divExact and not just primitive division.


Perhaps there are similarities to Scala, from my anecdotal observation. Coming from Java and doing the Scala coursera course years ago, it feels like arriving in a candy shop. All the wonderful language features are there, true power yours to wield. And then you bump into the code lines crafted by the experts, and they are line for line so 'smart' they take a real long time to figure out how the heck it all fits together.

People say "Rust is more complex to onboard to, but it is worth it", but a lot of the onboarding hurdle is the extra complexity added in by experts being smart. And it may be a reason why a language doesn't get the adoption that the creators hoped for (Scala?). Rust does not have issues with popularity, and the high onboarding barrier, may have positive impact eventually where "Just rewrite it in Rust" is no more, and people only choose Rust where it is most appropriate. Use the right language for the tool.

The complexity of Rust made me check out Gleam [0], a language designed for simplicity, ease of use, and developer experience. A wholly different design philosophy. But not less powerful, as a BEAM language that compiles to Erlang, but also compiles to Javascript if you want to do regular web stuff.

[0] https://gleam.run


At least from what I’ve seen around me professionally, the issue with most Scala projects was that developers started new projects in Scala while also still learning Scala through a Coursera course, without having a FP background and therefore lacking intuition/experience on which technique to apply when and where. The result was that you could see “more advanced” Scala (as per the course progression) being used in newer code of the projects. Then older code was never refactored resulting in a hodgepodge of different techniques.

This can happen in any language and is more indicative of not having a strong lead safeguarding the consistency of the codebase. Now Scala has had the added handicap of being able to express the same thing in multiple ways, all made possible in later iterations of Scala, and finally homogenised in Scala 3.


I agree. IMO, Scala can be written in Li Haoyi's way and it's a pleasure to work with. However, the FP and Effect Scala people are too loud and too smart that if I write Scala in Li Haoyi's way, I feel like I'm too stupid. I like Rust because of no GC, no VM and memory safe. If Rust has features that a Joe java programmer like me can't understand, I guess it'll be like Scala.

I honestly just don't believe that Rust is more complex to onboard to compared to languages like Python. It just does not match my experience at all. I've been a professional rust developer for about three years. Every time I look at python code, it's doing something insane where the function argument definition basically looks like line noise with args and kwargs, with no types, so it's impossible to guess what the parameeters will be for any given function. Every python developer I know makes heavy use of the repl just to figure out what methods they can call on some return value of some underdocumented method of a library they're using. The first time I read pandas code, I saw something along the lines of df[df["age"] < 3] and thought I was having a stroke. Yet python has a reputation for being easy to learn and use. We have a python developer on our team and it probably took me about a day to onboard him to rust and get him able to make changes to our (fairly complicated) Rust codebase.

Don't get me wrong, rust has plenty of "weird" features too, for example higher rank trait bounds have a ridiculous syntax and are going to be hard for most people to understand. But, almost no one will ever have to use a higher rank trait bound. I encounter such things much more rarely in rust than in almost any other mainstream language.


The language itself is not more complex to onboard. For Scala also not. It feels great to have all these language features to ones proposal. The added complexity is in the way how expert code is written. The experts are empowered and productive, but heightens the barrier of entry for newcomers by their practices. Note that they also might expertly write more accessible code to avoid the issue, and then I agree with (though I can't compare to Python, never used it).

Hm, you claim that Rust and Scala are not more complex to onboard than Python... but then you say you never used Python? If that's the case, how do you know? Having used both, I do think Rust is harder to onboard, just because there is more syntax that you need to learn. And Rust is a lot more verbose. And that's before you are exposed to the borrow checker.

I am not mentioning Python at all. I contrasted Rust with Scala.

Well, the parent wrote "I honestly just don't believe that Rust is more complex to onboard to compared to languages like Python." And you wrote "The language itself is not more complex to onboard." So... to contract Rust with Scala, I think it's clearer to write "The language itself is not more complex to onboard _than Scala_."

To that, I completely agree! Scala is one of the most complex languages, similar to C++. In terms of complexity (roughly the number of features) / hardness to onboard, I would have the following list (hardest to easiest): C++, Scala, Rust, Zig, Swift, Nim, Kotlin, JavaScript, Go, Python.


I see the confusion. ChadNauseam mentions Python to another comment of mine, where I mentioned Gleam. In your list hardest-to-easiest perhaps Gleam is even easier than Python. They literally advertise it as "the language you can learn in a day".

Thanks a lot! I wasn't aware of Gleam, it really seems simple. I probably wouldn't say "learn in a day", any I'm not sure if it's simpler than Python, but it's statically typed, and this adds some complexity necessarily.

> I honestly just don't believe that Rust is more complex to onboard to compared to languages like Python.

Most people conflate "complexity" and "difficulty". Rust is a less complex language than Python (yes, it's true), but it's also much more difficult, because it requires you to do all the hard work up-front, while giving you enormously more runtime guarantees.


Doing the hard work up front is easier than doing it while debugging a non-trivial system. And there are boilerplate patterns in Rust that allow you to skip the hard work while doing throwaway exploratory programming, just like in "easier" languages. Except that then you can refactor the boilerplate away and end up with a proper high-quality system.

> And then you bump into the code lines crafted by the experts, and they are line for line so 'smart' they take a real long time to figure out how the heck it all fits together.

Thing is, the alternative to "smart" code that packs a lot into a single line is code where that line turns into multiple pages of code, which is in fact worse for understanding. At least with PL features, you only have to put in the work once and you can grok how they're meant to be used anywhere.


I have no argument against using the right tool for a job. Decorating a function with a keyword to have more compile-time guarantees does sound great, but I bet it comes with strings attached that affect how it can be used which will lead to strange business logic. Anecdotally, I have not (perhaps yet) run into a situation where I needed more language features, I felt rust had enough primatives that I could adapt the current feature set to my needs. Yes, at times I had to scrap what I was working on to rewrite it another way so I can have compile-time guarantees. Yes, here language features offer speed in implementing.

Could you share a situation where the behavior is necessary? I am curious if I could work around it with the current feature set.

Perhaps I take issue with peers that throw bleeding edge features in situations that don't warrant them. Last old-man anecdote: as a hobbyist woodworker it pains me to see people buying expensive tools to accomplish something. They almost lack creativity to use their current tools they have. "If I had xyz tool I would build something so magnificent" they say. This amounts to having many, low-quality single-purpose built tools where a single high-quality table saw could fit the need. FYI, a table-saw could suit 90% of your cutting/shaping needs with a right jig. I don't want this to happen in rust.


> Could you share a situation where the behavior is necessary?

The effects mentioned in the article are not too uncommon in embedded systems, particularly if they are subject to more stringent standards (e.g., hard realtime, safety-critical, etc.). In such situations predictability is paramount, and that tends to correspond to proving the absence of the effects in the OP.


Ah, the embedded application. Very valid point. I'm guilty of forgetting about that discipline.

I do wonder if it is possible to bin certain features to certain, uh, distributions(?), of rust? I'm having trouble articulating what I mean but in essence so users do not get tempted to use all these bells and whistles when they are aimed at a certain domain or application? Or are such language features beneficial for all applications?

For example, sim cards are mini computers that actually implement the JVM and you can write java and run it on sim cards (!). But there is a subset of java that is allowed and not all features are available. In this case it is due to compute/resource restrictions, but something to a similar tune for rust, is that possible?


I guess the no_std/alloc/std split is sort of like what you're talking about? It's not an exact match though; I think that split is more borne out of the lack of built-in support some targets have for particular features rather than trying to cordon off subsets of the language to try to prevent users from burning themselves.

On that note, I guess one could hypothetically limit certain effects to certain Rust subsets (for example, an "allocates" effect may require alloc, a "filesystem" effect may require std, etc.), but I'd imagine the general mechanism would need to be usable everywhere considering how foundational some effects can be.

> Or are such language features beneficial for all applications?

To (ab)use a Pixar quote, I suppose one can think of it as "not all applications may need these features, but these features should be usable anywhere".


> Could you share a situation where the behavior is necessary? I am curious if I could work around it with the current feature set.

But this kinda isn't about "behavior" of your code; it's about how the compiler (and humans) can confidently reason about your code?


Sorry I don't understand. The result we all want is at compile-time ensuring some behavior cannot happen during runtime. OP argues we need for features built into the language, I am trying to understand what behavior we cannot achieve with the current primatives. So far the only compelling argument is embedded applications have different requirements (that I personally cannot speak to) that separate their use case from, say, deploying to a server for your SaaS company. No doubt there are more, I am trying to discover them.

I am biased to think more features negatively impact how humans can reason about code, leading to more business logic errors. I want to understand, can we make the compiler understand our code differently without additional features, by weidling mastery of the existing primatives? I very well may be wrong in my bias. But human enginuity and creativity is not to be understated. But neither should lazyness. Users will default to "out of box" solutions over building with language primatives. Adding more and more features will dilute our mastery of the fundamentals.


For example, in substrate-based blockchains if you panic in runtime code, the chain is effectively bricked, so they use hundreds of clippy lints to try to prevent this from happening, but you can only do so much statically without language level support. There are crates like no_panic however they basically can't be used anywhere there is dynamic memory allocation because that can panic.

Same thing happens in real time trading systems, distributed systems, databases, etc., you have to design some super critical hot path that can never fail, and you want a static guarantee that that is the fact.


You can see:

* no-panic: https://docs.rs/no-panic/latest/no_panic/

* Safe Rust has no undefined behavior: https://news.ycombinator.com/item?id=39564755


would be great to have no alloc too as op requested.

Look, included in the concepts the article discusses are features that people have been wanting for a very long time.

Like, the ability to have multiple mut references to a struct as long as you access disjoint fields? That's amazing!!! A redo of Pin that actually composes with the rest of the language? That's pretty awesome too.

I think you're getting tied up because the the author is describing these features in a very formal way. But someone has to think about these things, especially if they are going to implement them.

Ultimately, these are features that will make Rust's safety features (which really, are Rust's reason for existing) more ergonomic and easier to use. That's the opposite of what you fear.


> I genuinely struggled to follow the concepts in this article

I read the HN comments before I read the OP, which made me worry that the post was going to be some hifalutin type wonkiness. But instead the post is basically completely milquetoast, ordinary and accessible. I'm no type theorist--I cannot tell you what a monad is--and I confess that I don't understand how anyone could be intimidated by this article.


I think you're underestimating your competency

Things like effects aren't obvious to people, at least based on my experience of trying to teach it to people


I don't agree, the article isn't going deep into effects.

The intro where they describe effects as essentially being "function colors" (referring to another article fairly often linked in hackernews) plus give lots of concrete examples (async, const, try) seems like more than enough to be obvious to the readers.


I've been on both sides of the fence here - I've bounced between two camps:

1. Go with a better type system. A compiled language, that has sum types, no-nil, and generics.

2. A widely used, production, systems language that implements PL-theory up until the year ~2000. (Effects, as described in this article, was a research topic in 1999).

I started with (1), but as I started to get more and more exposed to (2), you start looking back on times when you fought with the type system and how some of these PL-nerds have a point. I think my first foray into Higher-Kinded Types was trying to rewrite a dynamic python dispatch system into Rust while keeping types at compile time.

The problem is, many of these PL-nerd concepts are rare and kind of hard to grok at first, and I can easily see them scaring people off from the language. However I think once you understand how they work, and the PL-nerds dumb down the language, I think most people will come around to them. Concepts like "sum types" and "monads", are IMO easy to understand concepts with dumb names, and even more complex standard definitions.


> 1. Go with a better type system. A compiled language, that has sum types, no-nil, and generics.

I was looking for something like that and eventually found Crystal (https://crystal-lang.org) as a closest match: LLVM compiled, strong static typing with explicit nulls and very good type inference, stackfull coroutines, channels etc.


Go does not use LLVM at all and never has. It uses its own compiler and its own (quirky) assembler. Of course, this does not matter to most people, but LLVM compilation can be both a blessing (good interop with C/FFI) and a curse (changes to LLVM internals causing issues for languages other than C/C++).

Crystal is a typed Ruby. The closer to Go language would probably be Odin.

Odin is more of an alternative C than Go. V is inspired by Go has like nearly the same syntax as Go and alot of Goodies. Like Error Types, Sum Types and more.

Crystal’s syntax is similar to Ruby’s, but AFAIK the similarity more-or-less ends there.

Yeah, (non sarcastically) give those things dumb names like borrow checker and you'll get people swooning over it.

A state machine is a perfect example of a case where you would benefit from linear types.

Some things just need precise terminology so humans can communicate about them to humans without ambiguity. It doesn't mean they're inherently complex: the article provides simple definitions. It's the same for most engineering, science and language. One of the most valuable skills I've learned in my career is to differentiate between expressions, statements, items, etc. - how often have you heard that the hardest problem in software development is coordinating with other developers? If you learn proper terminology, you can say exactly what you mean. Simple language doesn't mean more clear.

I wasn't born knowing Rust, I had to learn it. So I'm always surprised by complaints about Rust being too complex directed at the many unremarkable people who have undergone this process without issues. What does it say, really? That you're not as good as them at learning things? In what other context do software people on the internet so freely share self-doubt?

I also wonder about their career plans for the future. LLMs are already capable of understanding these concepts. The tide is rising.


At the risk of sounding like a language zealot, have you ever looked at Ada? It was explicitly designed to be very readable, and for use in safety-critical systems. Ada isn't perfect in all the ways Rust isn't, and it might not be the right choice for your system, but if you're writing systems software it's worth a look. If you're writing a web backend on the other hand, it's not worth a look at all.

Unless you are going back to CGI, web backends are systems.

I don't think this is an old man rant, I think you made a reasonable argument. Rust is certainly at risk of becoming just as complex as c++.

I would love to introduce more rust at work, but I dread that someone is going to ask about for<'a>, use<'a>, differences between impl X vs Box<dyn X>, or Pin/Unpin, and I don't have proper answers either.


> but I dread that someone is going to ask about for<'a>, use<'a>, differences between impl X vs Box<dyn X>, or Pin/Unpin, and I don't have proper answers either.

its an issue in "someone", all those answers can be received in under 1min from AI.


Rust is complex enough as-is. There is definitely a learning curve.

But these top-comments sometimes paint with a broad brush. As in this case.

> I admit the skill issue on my part, but I genuinely struggled to follow the concepts in this article. Working alongside peers who push Rust's bleeding edge, I dread reviewing their code and especially inheriting "legacy" implementations. It's like having a conversation with someone who expresses simple thoughts with ornate vocabulary. Reasoning about code written this way makes me experience profound fatigue and possess an overwhelming desire to return to my domicile; Or simply put, I get tired and want to go home.

Two paragraphs in and nothing concrete yet. We can contrast with the article. Let’s just consider the Effects section.

It describes four examples: functions that 1) don’t unwind, 2) guaranteed termination, 3) are deterministic 4) do not “call host APIs”, which is “IO” somehow? (this last one seems a bit off)

The first point is about not panicking (keyword panic given). Point two is about not looping forever, for example. Point three can be contrasted with non-determinism. Is that jargony? A fancy-pants term for something simpler? The fourth point seems a bit, I don’t know, could be rewritten.

All of these at least attempt to describe concrete things that you get out of an “effects system”.

> Rust's safety guardrails are valuable until the language becomes so complex that reading and reasoning about _business_ logic gets harder, not easier. It reminds me of the kid in "A Christmas Story" bundled so heavily in winter gear he cant put his arms down[0]. At some point, over-engineered safety becomes its own kind of risk even though it is technically safer in some regards. Sometimes you need to just implement a dang state machine and stop throwing complexity at poorly thought-through solutions. End old-man rant.

This is just a parade of the usual adjectives with an unexplained analogy thrown in (how will these additions cripple Rust usage?). “So complex”, “over-engineered safety”, “complexity” (again), “poorly thought-throught solutions”.

TFA is about concrete things. OP here is a bunch of adjectives. And a bunch of words about complexity and not understanding would be fine if TFA did not have any understandable, practical parts. But as we’ve gone over it does...

People see Rust. Then they see a comment reacting to nondescript complexity. The rest is history.

A good anti-complexity comment would address something concrete like Async Rust. And there are plenty of such comments to vote on.


As I was getting into computers as a teenager, a teacher warned me that the area is such that I would have to learn new things constantly, without end. I said, fine, what's not to like?

But I now realize that as people grow, their desire to learn new things sometimes fades, and an illusion of "already knowing enough" may set in.

Don't trust that illusion. We still have to learn new things every day. (And new fancy words for simple concepts is the easy part.)


> Reasoning about code written this way makes me experience profound fatigue and possess an overwhelming desire to return to my domicile;

I didn't understand that you were making fun of verbosity until the word 'domicile'. I must be one of those insufferable people who expresses simple thoughts with ornate vocabulary...

The article was comprehensible to me, and the additional function colorings sound like exciting constraints I can impose to prevent my future self from making mistakes rather than heavy winter gear. I guess I'm closer to the target audience?


Can we get a version of Rust that swaps lifetimes and ownership for a GC and a JS-style event loop? I love the DX of the language, but I don't always need to squeeze out every microsecond of performance at the cost of fighting the borrow checker.


ReasonML if you want a slightly more Rustic syntax.


> I love the DX of the language, but I don't always need to squeeze out every microsecond of performance at the cost of fighting the borrow checker.

you can use smart pointers everywhere and stop be bothered by borrow checker.


I mean, you're asking for a fundamentally different directing for the language with different tradeoffs? Why are you commenting on an article about Rust? Not everyone wants the same tradeoffs that you do.

Then why are you using rust for these tasks?

I'm not.

I experienced the same kind of fatigue when I read "there are three directions of development which I find particularly interesting: [three things I never heard about nor particularly want to get familiar with]" in the article.

And later, when I read "Because though we’re not doing too bad, we’re no Ada/SPARK yet" I couldn't help thinking that there must be a reason why those languages never became mainstream, and if Rust gets more of these exciting esoteric features, it's probably headed the same way...


I really think that golang makes it easy to read code, rust makes it easy to write code. If Golang had sum types it would be a much nicer language to write complex applications with

I find Go code mind numbing to read. There's just _so much of it_ that the parts of the code that should jump out at me for requiring greater attention get lost in the noise. Interfaces also make reading Go more difficult than it could be without LSP - there's no `impl Xyz for` to grep for.

It's the complete opposite for me. Rust code, especially async Rust code is just full of noise the only purpose of which is to make the borrow checker shut up

Go makes it easy to read each line of code, not necessarily to understand what the system as a whole is doing.

Go does have sum types — but the syntax is awkward and a bit transparent, so many don't recognize it as being there, and those that do don't love using it.

Can you enlighten us to what you’re talking about instead of vagueposting like this? What’s the supposed way of simulating sum types in go?

I am not sure how would you would simulate them. With enums and structs, I suppose?

However, it also has true sum types:

    type SumType interface { isSumType() }
    type A string
    func (A) isSumType()
    type B int
    func (B) isSumType()
But as you can see the syntax leaves a lot to be desired and may not be all that obvious to those who are hung up thinking in other languages.

This forms a closed set of types (A, B, nil -- don't forget nil!) but the compiler doesn't understand it as such and complains that the following type-switch is not exhaustive ("missing return"):

  func Foo(s SumType) bool {
    switch s.(type) {
    case A: return true      
    case B: return true
    case nil: return true
    }
  }
Also, you, the package author, may know what constitutes SumType, but the consumers of your package don't, at least not without source. Moreover, you can spread A, B, and any other implementations of SumType across many source files, making it hard to answer the question even with source. This is even a problem for the standard library, just consider go/ast and its Decl, Expr, and Stmt interfaces, none of which document what types actually implement them.

> but the compiler doesn't understand ...

Right — While it does has sum types, it doesn't have some other features found in other languages.

But, of course, if one wanted those features they would talk about those features. In this discussion, we're talking specifically about sum types, which Go most definitely does have.

> nil -- don't forget nil!

This is why alternative syntax has never been added. Nobody can figure out how to eliminate nil or make it clear that nil is always part of the set in a way that improves upon the current sum types.


interfaces in go aren’t types, so no, that’s not a sum type, it’s just an interface.

The set of objects that can fulfill that interface is not just string and int, it’s anything in the world that someone might decide to write an isSumType function for.


Interfaces in Go are structurally typed but they're still types. A variable of an interface type has two components: a pointer to its dynamic type information, including virtual method table, and a pointer to its value. When you consider that any compiled Go program has a finite set of known types concretely implementing each of its interfaces, they essentially become discriminated unions, albeit without Rust's compact inline representation (unless the dynamic type is itself a thin pointer).

> it’s anything in the world that someone might decide to write an isSumType function for.

No. Notice the lowercase tag name. It is impossible for anyone else to add an arbitrary type to the closed set.

Unless your argument is that sum types fundamentally cannot exist? Obviously given a more traditional syntax like,

   type SumType tagged {
      A | B
   }
...one can come along and add C just the same. I guess that is true in some natural properties of the universe way. It is a poor take in context, however.

Golang makes easy-to-skim code, with all the `if err != nil` after every function call.

Rust requires actual reading, like Typescript, only more detailed.


> I got tired of nil checks in Go and became a squeaky wheel in incident retros, where I finally got the chance to rewrite parts of our system in Rust during a refactor.

At a new job, I am writing my first microservice in golang. Used to be a Rust/C++ (kernel) and Python/PHP/JS dev (fullstack). Rust is allowed by team is heavily invested in go already.. I don't think I'll be able to convince them to learn rust! Lol


I think the misunderstanding here is that the article was not intended to users but to other language designers.

As a user, using a feature such as pattern types will be natural if you know the rest of the language.

Do you have a function that accepts an enum `MyEnum` but has an `unreachable!()` for some variant that you know is impossible to have at that point?

Then you can accept a `MyEnum is MyEnum::Variant | MyEnum::OtherVariant` instead of `MyEnum` to tell which are the accepted variants, and the pattern match will not require that `unreachable!()` anymore.

The fact someone does not know this is called "refinement types" does not limit their ability to use the feature effectively.


> the article was not intended to users but to other language designers.

That might be true, but it shows the direction that Rust is talking: put in the kitchen sink, just like C++ and Scala did. And _that_ is very much important for users.


I'm not sure it shows that. Even basic features of Rust we take from granted come from concepts common users do not need to understand. Borrowing of lifetime draws from affine types, but nobody cares when writing Rust code. If in 2012 you read a similar article explaining borrow checking in academic terms you would have thought Rust would be unusably hard, which is not.

Also I do not think that adding features is always bad to the point of comparing with Scala. Most of the things the article mentions will be almost invisible to users. For example, the `!Forget` thing it mentions will just end up with users getting new errors for things that before would have caused memory leaks. What a disgrace!

Then, pattern types allow you to remove panics from code, which is super helpful in many critical contexts where Rust is used in production, even in the Linux kernel once they will bump the language version so far.


> If in 2012 you read a similar article explaining borrow checking in academic terms you would have thought Rust would be unusably hard, which is not.

Ironically, Rust was unusably hard prior to the late-2018 edition. So now your academic article has to explain both the baseline borrow checker and non-lexical lifetimes, a vast increase in complexity.


Your statement contradicts itself. It was unusable hard before non-lexical lifetimes, but they vastly increased the complexity? Then maybe what’s complex for compiler writers to implement can make the user’s life easier by lowering the complexity of their code?

Nothing can really save you from architecture astronauts, except possibly Go, but I hear there are people templating Go with preprocessors, so who knows. This is a human problem. On the other hand I hear you. The moment Rust gets proper async traits an entire world of hexagonal pain will open up for the victims of the astronauts. So it will get even worse, basically. I think if we could solve the problem of bored smart people sabotaging projects it'd be amazing.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: