[m-users.] What makes this mostly_unique? Is there a quicker route through IO?

Zoltan Somogyi zoltan.somogyi at runbox.com
Sun Dec 6 14:37:09 AEDT 2020

On Sat, 5 Dec 2020 22:17:25 -0500, "Jeremy W. Sherman" <jeremyw.sherman at gmail.com> 
> main(!IO) :-
> Path = "does not exist",
> (if
> io.open_input(Path, ok(_), !IO)
> then
> io.write_line("ok", !IO)
> else
> io.write_line("err", !IO)
> ).

The condition of that if-then-else does two separate things:

1 attempt to open a file for input, and
2 check whether the attempt succeeded.

Doing part 2 in the condition is fine. Doing part 1 in the condition
is not fine, because it requires resurrecting a destroyed state of the world.

Assume the state of the world on entry to the condition is IO1.
The call to io.open_input destroys this state of the world, and gives you
back a new state, call it IO2. Then you check whether the result of the open
operation is ok(...). But if it isn't, then execution is supposed to continue
with the else part of the if-then-else, with the state of the world being
the same value it had at the time of the start of the if-then-else as a whole,
which means IO1. Since the condition has destroyed IO1, that can't happen.
That is what the error message is saying: that the I/O state that the condition
destroyed for forward execution was nevertheless needed on backtracking
to the else-part.

The fix is simple: move the call to io.open_input out of the condition.

In general, you cannot update the I/O state in any context that might fail.

> The only way I've found to work around this is to check each possible
> constructor, e.g. matching out both the ok(Stream) and err(_), even if
> I don't intend to do anything with the err(_) term itself.

That is by design. Far too many errors have been caused by programmers
not checking the return values of system calls and library calls.

> This
> quickly grows tedious when there's a sequence of operations, so if
> there's a more direct route, I am all ears!

That depends on what sequence of operations you are talking about.
For example, you can attempt to open several files with calls to io.open_input,
and then use the condition of a single if-then-else to check whether they all
returned ok. And it is easy to write a higher order function that takes a filename
and a predicate that operates on an input stream, which tries to open the
named file, invokes the supplied predicate on the resulting stream (and then
closes the stream) if the open operation succeeded, and does whatever
error handling is appropriate if the open failed.


More information about the users mailing list