Dealing with invalid filehandles (and maybe other invalid objects too)

You can check if something is a Failure by checking for truthiness or definedness without the Failure throwing:

for $*ARGFILES.handles -> $fh {
    say $fh if $fh;  # check truthiness
    .say with $fh;   # check definedness + topicalization
}

If you still want to throw the Exception that the Failure encompasses, then you can just .throw it.


TL;DR I thought Liz had it nailed but it seems like there's a bug or perhaps Ugh.

A bug?

It looks like whenever the IO::CatHandle class's .handles method reaches a handle that ought by rights produce a Failure (delaying any exception throw) it instead immediately throws an exception (perhaps the very one that would work if it were just delayed or perhaps something broken).

This seems either wrong or very wrong.

Ugh

See the exchange between Zoffix and Brad Gilbert and Zoffix's answer to the question How should I handle Perl 6 $*ARGFILES that can't be read by lines()?

Also:

  • https://github.com/rakudo/rakudo/issues/1313

  • https://github.com/rakudo/rakudo/search?q=argfiles&type=Issues

  • https://github.com/rakudo/rakudo/search?q=cathandle&type=Issues

A potential workaround is currently another bug?

In discussing "Implement handler for failed open on IO::CatHandle" Zoffix++ closed it with this code as a solution:

.say for ($*ARGFILES but role {
    method next-handle {
        loop {try return self.IO::CatHandle::next-handle}
    }
})

I see that tbrowder has reopened this issue as part of the related issue this SO is about saying:

If this works, it would at least be a usable example for the $*ARGFILES var in the docs.

But when I run it in 6.d (and see similar results for a 6.c), with or without valid input, I get:

say not yet implemented

(similar if I .put or whatever).

This is nuts and suggests something gutsy is getting messed up.

I've searched rt and gh/rakudo issues for "not yet implemented" and see no relevant matches.

Another workaround?

Zoffix clearly intended their code as a permanent solution, not merely a workaround. But it unfortunately doesn't seem to work at all for now.

The best I've come up with so far:

try {$*ARGFILES} andthen say $_    # $_ is a defined ArgFiles instance
                 orelse  say $!;   # $! is an error encountered inside the `try`

Perhaps this works as a black-and-white it either all works or none of it does solution. (Though I'm not convinced it's even that.)

What the doc has to say about $*ARGFILES

$*ARGFILES says it is an instance of

IO::ArgFiles which is doc'd as a class which

exists for backwards compatibility reasons and provides no methods.

And

All the functionality is inherited from

IO::CatHandle which is subtitled as

Use multiple IO handles as if they were one

and doc'd as a class that is

IO::Handle which is subtitled as

Opened file or stream

and doc'd as a class that doesn't inherit from any other class (so defaults to inheriting from Any) or do any role.

So, $*ARGFILES is (exactly functionally the same as) a IO::CatHandle object which is (a superset of the functionality of) an IO::Handle object, specifically:

The IO::CatHandle class provides a means to create an IO::Handle that seamlessly gathers input from multiple IO::Handle and IO::Pipe sources. All of IO::Handle's methods are implemented, and while attempt to use write methods will (currently) throw an exception, an IO::CatHandle is usable anywhere a read-only IO::Handle can be used.

Exploring the code for IO::CatHandle

(To be filled in later?)