betmatt has asked for the wisdom of the Perl Monks concerning the following question:

There is a similar post a few questions down. I have an interesting comparison with Python.
with open ('test.txt', 'n') as f print(f.mode) #f.close # not required
as compared to:

f = open ('test.txt','n') print(f.mode) f.close() # required
The first case will automatically exit the file when outside the block. The second case requires instruction. Is there an equivalence for Perl? What would the merits be of a similar approach in Perl?

Replies are listed 'Best First'.
Re: Automatically exiting a file within a block
by davido (Cardinal) on Jun 23, 2019 at 18:31 UTC

    Even more lexically constrained:

    print do {use autodie; open my $fh, '<', 'test.txt'; local $/ = undef; + <$fh>};

    The do {...} block forms a lexical scope. When the block exits, do returns the value of the last expression to execute. So in this case that expression is <$fh>, which reads the contents of the file opened with the $fh file handle. Then when the block terminates the lexical scope closes and $fh falls out of scope. When that happens, the file is closed.

    The use of autodie provides basic exceptions around the file IO operations, again constrained to the narrow lexical scope of the do {...} block.

    The nice thing that Python is providing here is a form of topicalization (for lack if me thinking up a better word). The with statement is creating that lexical scope, in a way, and topicalizing the file handle as f. Perl's native open doesn't return the handle; instead, it modifies its first arg to contain the handle. So it is inconvenient to come up with a proper Perl alternative that follows the same structure. But it's still possible using IO::File:

    use IO::File; for my $fh (IO::File->new('foo', 'r')) { print $fh->getlines; } # $fh->close is unnecessary.

    Here we're using for to topicalize the filehandle similar to what happens when with is used in Python.

    Even more tersely:

    use IO::File; print $_->getlines for IO::File->new('foo', 'r');

    Dave

Re: Automatically exiting a file within a block
by stevieb (Canon) on Jun 23, 2019 at 18:26 UTC

    If you open a file in a block, Perl will close it automatically when the scope of the block finishes:

    use warnings; use strict; { open my $fh, '<', 'a.txt' or die $!; # do stuff with file handle } # file handle is closed here # in fact, the $fh isn't even accessible here at all

    The block can be a bare block like I've shown, or it could be within a function, loop etc. Essentially, all file handles within any scope will automatically close themselves when the scope they're in finishes. If it's in file scope, it'll close itself when the program exits.

      I don't know if that's the case of Python, but C# uses a structure like the OP presented because it ensures timely destruction of the object.

      This isn't needed in Perl because Perl's garbage collector ensures that objects are destroyed as soon as they are no longer referenced. (Well, before the start of the next statement after the one in which they cease being referenced, anyway.)

      While this leads to simpler code in Perl, the C# (and maybe Python) approach is faster and can handle references cycles.

Re: Automatically exiting a file within a block
by haukex (Bishop) on Jun 23, 2019 at 19:52 UTC

    As the others have said, lexical filehandles are closed automatically when they go out of scope. This does not apply to bareword filehandles such as open FILE, ..., these are essentially globals. Note that you can arrange to have other code executed at the end of the current lexical scope via the End module, although I haven't seen much code in the wild actually doing so.

      This is of course still not quite what Python's context managers provide, although sufficient in the given, parochial case. In Python, and even though presented as "idiomatic" in pretty much every entry level tut, I'm hardly ever using it in cases like these, in part for the reasons mentioned. Very different from, say, the scope guards in D, where you use them all the time, if for no other reason than this being idiomatic there. That said, context managers in Python, to be fair, are a different kind of animal as they don't just have an exit method, but an enter method too, and those can be user-defined, so you can further generalize and do all kinds of things, like checking about resources beforehand, or providing some specific setup, etc. Perl's End, whether the ordinary scriptwide form or the mentioned extension, satisfies the latter, not the former, that would be sub-level "BEGIN". There is Wrap::Sub, for instance, that might be more of a Perl equivalent. Don't think it's particularly common either, however. It's just not how you write Perl, usually.