Yes, but your approach doesn't actually solve any of the requirements:
> things like checking at compile time all file handles are correctly opened before use, and closed after use
Using distinct `Open` and `Closed` types, as you say, doesn't help at all with ensuring file handles are closed after use.
They also don't ensure that handles are correctly opened before use, as I showed with my example `open "/tmp/foo" >>= (\o -> close o >> readFile o >>= putStr)`.
I can't think of a way to ensure both of these things, without using linear or dependent types to track state machines in types.
I can think of ways to ensure one of these things:
- To ensure files are correctly opened, we can remove the ability to close them. My above example would then work correctly, since `close o` would be a no-op, and `readFile` would succeed.
- To ensure all handles get closed, we can remove the ability to open them.
Separating handles into `Open` and `Closed` varieties does nothing more than provide documentation hints to programmers; it cannot automatically check whether handles are used correctly, it requires programmers to consciously avoid language features (like using variables multiple times), be careful about the way they compose functions, and write test suites to check if things are working. In other words, it provides none of the benefits of static typing, and is more akin to documentation in a dynamically typed language.