Hacker News new | past | comments | ask | show | jobs | submit login

>that's the wrong interface, because closing a file can error

Realistically, it shouldn't, and there's no way you would be able to handle it properly even if it did. Flush your FDs to ensure you get write errors before closing. And EBADF means there's a bigger problem in your program (you're incorrectly sharing FDs).




`closeFile` is a bad example. But here are better ones:

  File openFile(String path)
  File writeString(File file, String content)
Those signatures are completely wrong!

Opening or writing a file can reasonably fail in multiple ways.

The language does not handle any failing effects at all.


I left error handling out because it's an introductory post and the code would involve too many simultaneous concerns.


This may be a valid approach.

But than this should be communicated adequately, imho.

(And better, more realistic examples, that aren't glaring wrong, would be a good idea. But I fear all examples would than melt down to pure functions. But pure functions were never an issue. In any language).

I really understand why someone would try to build a better language. All the premises in the blog post are right! Only that I fear that the conclusion are frankly quite off. Looks like the usual misunderstanding of "KISS" to me, to be honest.

(And I don't like to be to harsh actually. I know how much work it is to come up with anything that works at all. Languages are a very unrewarding field of work sadly).


I wrote:

>(Error handling etc. omitted for clarity.)

I've thought about this at length. The conclusions are right.


> I wrote:

You wrote that in a later example, after several simpler examples not including error handling either, and not introducing error handling at all. In fact error handling is not covered anywhere by the introductory text.

The part which really grates is that this makes all the examples in the "motivation" section for linear types work against their very section: because all the destructors are infaillible, they're perfect motivations for an affine type system and implicit destructors, all they say is that a linear type system requires more work from the user for no visible value.


No, on the contrary: you can recover from destructor failure by returning error values, as in Haskell or Rust.

With affine types, destructors must be infallible, otherwise you get the double throw problem, as in C++ or Rust.

With error handling, the signatures would just be:

    function closeFile(file: File): Either[File, Unit];
Where returning a `File` means closing it failed. Then you can try again, or abort. And returning `Unit` means closing succeeded. You can bikeshed the exact return type endlessly.


...

That's literally my point, and what you are not demonstrating in this introductory text, and what the examples you wrote completely undermine.

> With error handling, the signatures would just be:

But they're not is the point, the text spends an entire section and a good 10 pages explaining linear types (thus clearly assuming the reader does not know about them), yet all the examples undermine what they're supposed to defend. If being already sold on linear types is necessary to be motivated by this section it's not doing a very good job.

And, again, no coverage or explanation of error handling anywhere in the introductory text, even though that's as important as it's divisive (even more so for a language focused on safety and correctness).

As originally noted I had to go hit up section 14 of the tutorial (and I can't say I'm impressed by the coverage that document provides, as it spends more time expounding about the philosophical categorisation of errors than actually demonstrating error handling capabilities and features).

> Where returning a `File` means closing it failed.

That's not a very useful error report, and probably wrong as well: in most situations the file handle is invalidated by closing it, even if the closure fails.

In POSIX, "EBADF" reports that the file was already broken, and "EIO" reports a flush error, which usually means that the resource has gone away e.g. it was on a transient or network device which is inaccessible.

"EINTR" is the only one for which is debated[0] and may leave the fd open, but even then whether any operation other than closing (again) can succeed in that case is not clear.

[0] https://www.austingroupbugs.net/view.php?id=529


And here we are. A language "focused on safety and correctness" where you need to argue because error handling is neglected instead of being put front, and not even the most trivial resource management is thought out (even big sounding terms like "linear types" and "capabilities" were thrown on the table).

Together with this joke to call async a "fashion" (in a time of networked services everywhere, and high performance I/O being now only possible with asynchronous APIs) this whole thing will go nowhere, imho.

Nice theoretical ideas aren't enough when it comes to programming languages. The execution is key.

This thing is a clear instance of misunderstanding of "the KISS principle": Dumbing things down to the max is not KISS! Because it neglects inherent complexity. KISS only wants to remove accidental complexity. All other complexities still need to get addressed accordingly. Nobody needs the next Go or V.


> A language "focused on safety and correctness" where you need to argue because error handling is neglected instead of being put front, and not even the most trivial resource management is thought out (even big sounding terms like "linear types" and "capabilities" were thrown on the table).

The OP is complaining about the introductory code samples and how they don't demonstrate the error handling idioms of the language, not any problems with how errors are handled in this language.


Presumably close should return either success or a broken_file of some sort. I can't think of a use case for getting back a file which looks like you can write to it when close failed previously.

Write might be similar. I think you want to return a file on success and some information that you might be able to do something with on failure, where there might be a path to retrieving a working file from it.

Easier to see, allocate could return a MaybeHeap which you branch/match on, where one path is call a garbage collector and try again.

This is all kind of messy though. Retry loops or memory cleanup attempts should perhaps be on the far side of the API.

Rust throws an exception if things go too badly wrong as far as I can tell, though the docs tell me panic! isn't an exception, and also how to catch it.


> Realistically, it shouldn't, and there's no way you would be able to handle it properly even if it did.

So you think it is better to continue execution completely oblivious of the issue? Or do you assume `close` will straight up terminate the program if it fails?

> Flush your FDs to ensure you get write errors before closing.

Nothing precludes implementing `closeFile` by flushing it, returning any error as a failure, then actually closing.




Join us for AI Startup School this June 16-17 in San Francisco!

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

Search: