News for hackers. Stuff that matters.
Re:The Inevitability of the Borrow Checker by brokencode on Friday February 07, 2025 @01:20PM (#42975774)
Inko is an interesting language, though the handling for panics sounds disturbing. Based on the documentation, panics abort the whole program and are not recoverable.

Things like dividing by zero or accessing an index outside of the bounds of an array will panic.

While these aren’t incredibly common, I would never want my whole production web server to go down because some random, infrequently used endpoint had a bug in some edge case.

The language seems heavily inspired by Erlang, which is very resilient to programmer errors in comparison.

In Erlang, the philosophy is to let a lightweight process crash and restart it when there is a problem. This doesn’t need to have any effect on other lightweight processes that are running.

Re:The Inevitability of the Borrow Checker by nixpulvis on Friday February 07, 2025 @05:40PM (#42978335)
That's why you have wrappers that return Result enumerations. There is an overhead to check if an operation is valid, pay for it if you need it.

This is exactly how Rust works, for example.

Re:The Inevitability of the Borrow Checker by brokencode on Friday February 07, 2025 @06:05PM (#42978539)
How is it knowing to panic if it isn’t checking? It seems like I am paying the cost of doing the checking either way, at least in terms of performance. So it should give me the ability to handle the error somehow.

As a C# and Typescript developer primarily, this concept of killing the whole program due to a bug anywhere in any part of the code is very foreign to me.

I’d expect that to only happen if you ran out of memory, had a hardware failure, or other such catastrophic error. To me, dividing by zero is a problem, but not worse than pretty much any other type of logical error that doesn’t panic.

Re:The Inevitability of the Borrow Checker by paulddraper on Friday February 07, 2025 @06:40PM (#42978781)
> killing the whole program due to a bug anywhere in any part of the code is very foreign to me.

But getting the wrong answer due to a bug anywhere in any part of the code is not?

Re:The Inevitability of the Borrow Checker by brokencode on Friday February 07, 2025 @07:31PM (#42979133)
Programs have all kinds of bugs for all kinds of reasons. That’s why you need unit testing, functional testing, performance testing, usability testing, accessibility testing, etc.

Why single out just a few types of bugs to crash the whole program? Let me catch it and decide what to do.

If it’s in a single request to my web server, I’d like that single request return an error code. I would not like all other concurrent requests for all other users to fail as well.

Re:The Inevitability of the Borrow Checker by ludwik on Friday February 07, 2025 @07:00PM (#42978922)
Allowing you to catch and handle an exception is not the same as silently returning a wrong answer.
Re:The Inevitability of the Borrow Checker by caspper69 on Friday February 07, 2025 @07:22PM (#42979075)
It seems to me that exceptions have a bad rep.

Complaints range from too slow to hidden control flow to just not liking them.

I absolutely see the benefit in application code, otherwise you're error checking every function call.

On the other hand, I completely understand not using such a mechanism in kernel and driver code.

But Rust has decided against exceptions (which I actually believe were part of the original vision way back when, but please don't quote me), so now there's panics.

Everything is a tradeoff at some level.

Re:The Inevitability of the Borrow Checker by paulddraper on Friday February 07, 2025 @11:46PM (#42980461)
Yes, the wrong answer is terrifying
Re:The Inevitability of the Borrow Checker by demurgos on Friday February 07, 2025 @05:51PM (#42978428)
Rust's panics are recoverable, so they're more like exceptions. This means that you may need to care about "unwind" safety and values in a potentially invalid state. It's the main reason for mutex poisoning and having to deal with it when locking.

Overall, I'm not entirely sure if Rust would be better or not if panics were non-recoverable.

PS: For completeness, there are flags to control the panic behavior; but you can't rely on them when writing a lib.

Re:The Inevitability of the Borrow Checker by haberman on Friday February 07, 2025 @07:56PM (#42979276)
The Rust docs specifically discourage using panics as try/catch: https://doc.rust-lang.org/std/panic/fn.catch_unwind.html

> It is not recommended to use this function for a general try/catch mechanism. The Result type is more appropriate to use for functions that can fail on a regular basis. Additionally, this function is not guaranteed to catch all panics, see the “Notes” section below.

I think panics in Rust are intended to be unrecoverable, and catch_unwind is mainly intended to make the shutdown more orderly and possibly localized. Any data structure that paniced is probably in an unknown state that is not safe for further use.

Re:The Inevitability of the Borrow Checker by brokencode on Friday February 07, 2025 @08:37PM (#42979532)
The situation in Rust seems to be a bit complicated. But I did find that panicking within a thread you spawn kills just that thread and not the whole program. And it seems that Tokio has the same behavior.

https://github.com/tokio-rs/tokio/issues/2002#issuecomment-6...

And web frameworks like Tower provide standard ways of handling panics and turning them into error responses.

https://docs.rs/tower-http/latest/tower_http/catch_panic/ind...

So I don’t think panics are necessarily meant to be program-killing in Rust, even if Result types are heavily recommended instead.

Re:The Inevitability of the Borrow Checker by Too on Saturday February 08, 2025 @06:59AM (#42982355)
https://doc.rust-lang.org/nomicon/unwinding.html explains the philosophy and how they got there a bit further. Rust grew from a short-lived task concept, where panic of a task wouldn't be the end of the world, then it got used to write bigger applications instead and finer catch-concepts were needed.
Re:The Inevitability of the Borrow Checker by J_Shelby_J on Sunday February 09, 2025 @02:10PM (#42992818)
Catch unwind is useful when you have a dependency that for some reason panics and you can’t control it.
Re:The Inevitability of the Borrow Checker by ryang2718 on Friday February 07, 2025 @06:03PM (#42978528)
Unless a library developer decides to abort on panic in the `toml`, then i don’t believe you can unwind.
Re:The Inevitability of the Borrow Checker by YorickPeterse on Friday February 07, 2025 @06:27PM (#42978676)
Panics are meant to be used sparingly and as a last resort, and are meant to signal "this code is utterly broken, go fix it". Handling such errors at runtime (e.g. by restarting just a lightweight process) doesn't make much sense, as you'll keep running into the issue over and over again and possibly even end up ignoring it.

For runtime error handling you'd use algebraic types such as Result and Option, similar to most functional languages and Rust.

The reason we don't allow catching of panics (such as Rust allows) is because it can result in people more or less ignoring such errors (something I've seen happen far too often in past projects) and just retrying over and over again, achieving the same result every time. I'd much rather have the program crash and scream loudly, forcing developers to address the issue. Fortunately, the chances of you actually running into a panic at runtime should be pretty slim, so not being able to catch them shouldn't be much of an issue in practice.

Re:The Inevitability of the Borrow Checker by brokencode on Friday February 07, 2025 @07:16PM (#42979028)
Thats fair, and it’s your language to design how you think is best. Just throwing out my two cents here based on my personal experiences.

I have 2000+ developers at my work, many of them making changes on a single huge web application. Compile/publish of the testing servers takes several hours and happens regularly throughout the day. There are thousands of testers all testing many different workflows all the time.

If one developer makes a mistake in a dark corner of the code one day, it seems bad to have the whole server crashing and disrupting all the other testing that’s going on for multiple hours while we wait for a fix and another build.

Quality and fix prioritization is in the hands of project teams for usability problems, performance problems, functional problems, etc. Uncaught exceptions should be logged somewhere, and it’s ultimately up to the team to decide whether they care enough to fix them or not.

Re:The Inevitability of the Borrow Checker by someone654 on Saturday February 08, 2025 @07:36AM (#42982532)
> have the whole server crashing

You do not need to solve that at the language level. A common pattern is using multiple replicas, service discovery and automatic restart upon a dead replica. For example kubernetes does this out of the box.

For dark corners of the code, that is often a good middle ground between interrupting the service and never die.

Re:The Inevitability of the Borrow Checker by brokencode on Saturday February 08, 2025 @02:41PM (#42985591)
Sure, it's a good idea to have something like that just in case. But crashing is really not desirable and could cause all kinds of problems, even if you do have recovery mechanisms in place.

A crash in a web server could result in data loss, timeouts and performance issues on clients due to the response never coming back, crashes on poorly written clients, cascading failures in poorly written services, etc.

And for a client application, imagine if your web browser would just randomly crash sometimes. Do you think it would make it much better if the browser would detect the crash and relaunch itself? And what if it relaunches itself and then crashes again for the same reason?

Re:The Inevitability of the Borrow Checker by nicoburns on Friday February 07, 2025 @07:15PM (#42979027)
I feel like if that's your take, then things like array indexing or dividing by zero should never panic. Because running into those things at runtime is very hard to prevent (unless you have tooling for enforcing that statically?)
Re:The Inevitability of the Borrow Checker by YorickPeterse on Friday February 07, 2025 @07:37PM (#42979181)
There are cases where you might want to choose: crash and burn, or handle at runtime. Where this makes sense, Inko does actually let you do that. For example, array indexing is done using one of two methods:

- Array.get/Array.get_mut for when you want to panic on an out-of-bounds index

- Array.opt/Array.opt_mut for when you want an Option type, with it being a None for an out of bounds index

So for example:

    [10, 20, 30].get(0)  => 10
    [10, 20, 30].get(42) => panic
    [10, 20, 30].opt(0)  => Option.Some(10)
    [10, 20, 30].opt(42) => Option.None
This pattern is applied across the standard library where this makes sense, such that the developer can pick which option works best for their needs.

There are a few places where we check for some input at runtime and panic if this is invalid, but this is limited to cases where there's simply no sensible alternative (e.g. providing a key with an invalid size to a ChaCha20 cipher).

Re:The Inevitability of the Borrow Checker by dullcrisp on Friday February 07, 2025 @11:20PM (#42980353)
If panics can’t be handled at runtime they need to be impossible to introduce. Saying they just need to be fixed immediately might make sense for a project with a handful of developers but as others have said a major production system failing due to a panic wouldn’t be acceptable.
Re:The Inevitability of the Borrow Checker by Too on Saturday February 08, 2025 @06:46AM (#42982306)
With those two names it's rather inevitable that any beginner of the language will use the obvious sounding, but panic-inducing get.

That's not scalable if it's not possible to catch panics. If panics can't be handled they must only be caused by super corner 0.001% cases, not by fundamental things like dividing an integer or indexing an array.

Re:The Inevitability of the Borrow Checker by Muximize on Saturday February 08, 2025 @11:13AM (#42983929)
I think this is bad api design. The opt method should not exist, and the get method should return option.
Re:The Inevitability of the Borrow Checker by RossBencina on Friday February 07, 2025 @08:46PM (#42979579)
On the topic of "sparingly and as a last resort" I agree, and I think there is more to it. There are situations where it makes sense to panic and there are situations where it doesn't make sense. There can be policies that guide when panic is/isn't appropriate.

In an open source library that I maintain, the C code makes use of asserts. These asserts are essentially unrecoverable panics on constraint/invariant violation. The effect of an assert failure is that violation renders the code unrunnable ("utterly broken"). Unfortunately "panic, go fix it" turns out to not be a great fit for a widely distributed library for at least three reasons: (1) the user of a library is often not in a position to fix it, (2) the distribution is so far removed from the maintainers that the maintainers may or may not get the message to fix it, and even if they can fix it the turnaround time to the next distro release doesn't help the current user very much, (3) if the constraint violations can be triggered by external code then we are essentially introducing "transitive panic" for conditions that we can't possibly fix (we don't control or have visibility into external code, such as Windows APIs and drivers).

The upshot of this is that our policies are to only panic (i.e. use an assert) to check conditions/constraints/invariants that are under the full control of our own code base (never to confirm assumptions about externally supplied data). If we are in a context where it is practicable, prefer raising error conditions such as "internal error" or "unexpected native API response".

With the above in mind, I disagree with your point about supervision trees and restarting lightweight processes. Requiring panics to crash the whole process is the most extreme outcome possible, and can potentially lead to an extremely unsatisfactory user experience. Software is almost always performing many more functions than the little piece of code that panics. I find it difficult to believe that killing the whole process (i.e. unrecoverable panic) is universally the best course of action. Killing and permanently disabling the lightweight process would at least allow the rest of the program a chance to continue in some kind of limp mode until the issue can be fixed.

Looking at it another way, by killing the whole process, you are effectively limiting use to those cases where killing the whole process is indeed the only appropriate course of action.

On "you'll keep running into the issue over and over again and possibly even end up ignoring it" To me this smells over-opinionated and poorly justified. So long as there is good error reporting and diagnostics tracing, the user of the language can and should decide these things. But maybe it can be better justified and maybe there are plenty of use-cases that are fine with "no soup for you!" at the first sign of trouble.

Re:The Inevitability of the Borrow Checker by patrick451 on Saturday February 08, 2025 @02:15AM (#42981118)
I hope nobody ever uses inko for flight control software. Oh, your plane crashed because some hip language won't allow catching a panic? Don't worry, the developers are fixing it for the next release.

Sorry, but limping along is better than crashing.

Re:The Inevitability of the Borrow Checker by dllthomas on Wednesday February 19, 2025 @11:47PM (#43111242)
Limping along is sometimes better than crashing. It's highly context dependent.
Re:The Inevitability of the Borrow Checker by paulddraper on Friday February 07, 2025 @06:39PM (#42978776)
> I would never want my whole production web server to go down because some random, infrequently used endpoint had a bug in some edge case.

Then you need an alternative.

And that alternative is...messy. Do you have exceptions? Do you type them? Or do you have explicit return values? Is there syntax sugar? Etc, etc.

Re:The Inevitability of the Borrow Checker by brokencode on Friday February 07, 2025 @07:42PM (#42979206)
All error handling is messy in one way or another. I am personally a fan of the exceptions in C#. It’s good enough for me and doesn’t make me add a bunch of error types to all my methods.

I also like the Erlang philosophy of letting the lightweight process crash as soon as any problem occurs and having a supervisor decide what to do.

Re:The Inevitability of the Borrow Checker by BobaFloutist on Saturday February 08, 2025 @01:35PM (#42985052)
Ok so I'm a bit of a coding noob, but couldn't you hypothetically catch panics before they happen by having some boilerplate you insert in every case that could cause one?

I guess checking to make sure you aren't dividing by zero before you divide every time or checking an index access against the bounds of an array could have some performance implications, and also it would be prohibitively hard to actually enforce it across your team?

Re:The Inevitability of the Borrow Checker by brokencode on Saturday February 08, 2025 @02:29PM (#42985500)
Yup, that would be the fix. It is a logical error to even try to be dividing by zero, so your code should handle it. Regardless of whether there is a panic, this is what you'd want to do.

But the problem is that people make mistakes or forget every once in a while, and as you get more people working on large programs, these mistakes happen more often.

So you have to assume that it'll most likely happen in your production software sooner or later. Crashing the whole program is a fairly catastrophic outcome in my opinion, and should be reserved for only truly unrecoverable problems like hardware failures.

Re:The Inevitability of the Borrow Checker by rurban on Saturday February 08, 2025 @05:33PM (#42986767)
And how do want to deal with fatal errors?

Overflows and divide by zero are easily avoidable and should be fatal.

Even the Apollo lander program aborted and even caused a reboot. Such that the reboot loop caused an uncontrollable lander, just by overloading the IO. The scheduler could have killed the radar process, but this was Aldrin's job, and he failed, not the scheduler.