Ooof, that’s low!
I know the default soft limit for me is 1024, but the hard limit is ~half a million.
I think this depends on how much work is “held onto”. I don’t think it’s sensible to suggest that every time you think about a file you get a file descriptor and hold onto it. But while actively performing some unit of work it probably makes sense to hold onto a few handles. I don’t think this should be a problem even on systems like those you mentioned?
Quick one-off accesses could still be done with read(handle(path), String) etc. While we could of course make read(path, String) work too, I do think there’s a level of desirable friction to push people to write code that reuses handles whenever it’s sensible to do so.
With work like https://github.com/JuliaLang/julia/pull/45272 (and follow-ups) I wonder if handle could be implemented such that the compiler can perform eager finalisation for calls like this? (i.e. not rely on GC for closing the file descriptor).
I’m not sure if this is a great idea, because fundamentally a resource is a different beast to the path to a resource. We could have something like handle(h::AbstractHandle) -> h though, so that you can accept either and know that by calling handle(h) you end up with a handle. I’m half tempted to consider AbstractResource = Union{AbstractPath, AbstractHandle} but I think at this point we’re overcomplicating things. There’s definitely still room for improvement on the design though…
Yes, I’ve checked and I’m pretty confident this can be done (for a file descriptor) on all three major OSs.