35

I've seen some horrific code written in Perl, but I can't make head nor tail of this one:

select((select(s),$|=1)[0])

It's in some networking code that we use to communicate with a server and I assume it's something to do with buffering (since it sets $|).

But I can't figure out why there's multiple select calls or the array reference. Can anyone help me out?

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • 4
    To confuse and frustrate people who have to maintain the code, i.e. you. – Chris Lutz Feb 18 '10 at 09:41
  • please check [perldoc -f select](http://perldoc.perl.org/functions/select.html). For the meaning of `$|`, please check [perldoc perlvar](http://perldoc.perl.org/perlvar.html) – ghostdog74 Feb 18 '10 at 09:35

6 Answers6

67

It's a nasty little idiom for setting autoflush on a filehandle other than STDOUT.

select() takes the supplied filehandle and (basically) replaces STDOUT with it, and it returns the old filehandle when it's done.

So (select($s),$|=1) redirects the filehandle (remember select returns the old one), and sets autoflush ($| = 1). It does this in a list ((...)[0]) and returns the first value (which is the result of the select call - the original STDOUT), and then passes that back into another select to reinstate the original STDOUT filehandle. Phew.

But now you understand it (well, maybe ;)), do this instead:

use IO::Handle;
$fh->autoflush;
Dan
  • 61,568
  • 9
  • 61
  • 78
  • 1
    Sorry, I thought you meant nasty as in dodgy-functionality rather than lack-of-clarity. So it's passing the result of the inner select to the outer select, reselecting the original. That makes sense now but you're right, I'll probably scrap it and use autoflush. – paxdiablo Oct 13 '08 at 05:34
  • 19
    I wrote that ugly piece of code long before IO::Handle->autoflush existed. Please let it die a quiet death. :) – Randal Schwartz Oct 13 '08 at 05:48
  • 5
    @Randal: So *you're* to blame! My apologies for describing it as 'nasty', in the context of IO::Handle->autoflush not existing then it is indeed a crafty little chunk of code ;) (/me backpedals furiously) – Dan Oct 13 '08 at 06:52
  • 1
    I don’t see any reason to use the Lisp-ish version. Even in absence of IO::Handle you can write it much more nicely by using `for` to topicalise the old handle for re-selection. See my answer. – Aristotle Pagaltzis Oct 14 '08 at 05:42
  • `perl -wE 'open OUTPUT, ">", undef; say select(OUTPUT)'` doesn't print out main::STDOUT. Why? – David Farrell Jul 03 '15 at 16:01
  • @DavidFarrell because the first argument to `say` is a filehandle ;-) Try `say STDOUT select(OUTPUT)`. – Sinan Ünür Dec 29 '18 at 22:50
30

The way to figure out any code is to pick it apart. You know that stuff inside parentheses happens before stuff outside. This is the same way you'd figuring out what code is doing in other languages.

The first bit is then:

( select(s), $|=1 )

That list has two elements, which are the results of two operations: one to select the s filehandle as the default then one to set $| to a true value. The $| is one of the per-filehandle variables which only apply to the currently selected filehandle (see Understand global variables at The Effective Perler). In the end, you have a list of two items: the previous default filehandle (the result of select), and 1.

The next part is a literal list slice to pull out the item in index 0:

( PREVIOUS_DEFAULT, 1 )[0]

The result of that is the single item that is previous default filehandle.

The next part takes the result of the slice and uses it as the argument to another call to select

 select( PREVIOUS_DEFAULT );

So, in effect, you've set $| on a filehandle and ended up back where you started with the default filehandle.

brian d foy
  • 129,424
  • 31
  • 207
  • 592
21
select($fh)

Select a new default file handle. See http://perldoc.perl.org/functions/select.html

(select($fh), $|=1)

Turn on autoflush. See http://perldoc.perl.org/perlvar.html

(select($fh), $|=1)[0]

Return the first value of this tuple.

select((select($fh), $|=1)[0])

select it, i.e. restore the old default file handle.


Equivalent to

$oldfh = select($fh);
$| = 1;
select($oldfh);

which means

use IO::Handle;
$fh->autoflush(1);

as demonstrated in the perldoc page.

kennytm
  • 510,854
  • 105
  • 1,084
  • 1,005
  • 1
    If someone doesn't want to use `IO::Handle` the least they could do is wrap this monstrosity in a function like `sub flush ($) { select((select($_[0]), $|=1)[0]); }` – Chris Lutz Feb 18 '10 at 09:42
  • 1
    If you're going to put it in a function then you might as well write it the long way with the temporary to hold the old handle. :) – hobbs Feb 18 '10 at 09:46
  • @hobbs - Eh, I could go either way. For functions that simple, once it's in a function, you never really have to look at it again, nor is the functionality of a `flush` function ever likely to change. Perhaps the horrific version is slightly faster. Perhaps you can use it to scare an intern. – Chris Lutz Feb 18 '10 at 09:54
10

In another venue, I once proposed that a more comprehensible version would be thus:

for ( select $fh ) { $| = 1; select $_ }

This preserves the compact idiom’s sole advantage that no variable needs be declared in the surrounding scope.

Or if you’re not comfortable with $_, you can write it like this:

for my $prevfh ( select $fh ) { $| = 1; select $prevfh }

The scope of $prevfh is limited to the for block. (But if you write Perl you really have no excuse to be skittish about $_.)

Aristotle Pagaltzis
  • 112,955
  • 23
  • 98
  • 97
  • Both yours and Randall's versions are less than obvious, but at least Randall's has the advantage of being short.. Is there some reason why my $old_fh = select($fh); $| = 1; select($old_fh); doesn't work? As if it's comprehensibility you're going for it would seem a considerably more sensible choice – Dan Oct 14 '08 at 07:41
  • 1
    Did you see any claims about it not working? In any case if you actually want a sensible choice you’ll use IO::Handle. As for shortness, Randall’s has a 1–4 character advantage depending on whitespace. – Aristotle Pagaltzis Oct 14 '08 at 08:10
  • That was a completely honest question, by the way. I've never been in a position where I've had to do it (at least where IO::Handle hasn't been available). – Dan Oct 14 '08 at 08:30
  • 1
    So go ahead and use it. I didn’t claim that my solution was the best nor that any other solution doesn’t work; I merely offered, in the spirit of TMTOWTDI, a Perl-ish rather than Lisp-ish idiom. – Aristotle Pagaltzis Oct 14 '08 at 11:03
  • 1
    I like Aristotle's version better than Randall's because it doesn't rely on execution order in a list, which (having cut my teeth on Scheme) I am suspicious of. – Never Sleep Again Sep 06 '17 at 18:01
7

It's overly clever code for turning on buffer flushing on handle s and then re-selecting the current handle.

See perldoc -f select for more.

Andy Lester
  • 91,102
  • 13
  • 100
  • 152
3

It is overoptimization to skip loading IO::Handle.

use IO::Handle;
$fh->autoflush(1);

is much more readable.

Alexandr Ciornii
  • 7,346
  • 1
  • 25
  • 29