Christopher Bottoms Christopher Bottoms - 10 days ago 5
Perl Question

I can create filehandles to strings in Perl 5, how do I do it in Perl 6?

In Perl 5, I can create a filehandle to a string and read or write from the string as if it were a file. This is great for working with tests or templates.

For example:

use strict; use warnings;

my $text = "A\nB\nC\n";

open(my $fh, '<', \$text);

while(my $line = readline($fh)){
print $line;
}


How can I do that in Perl 6? The following doesn't work for Perl 6 (at least not for my instance of Perl6 running on MoarVM 2015.01 from the January 2015 release of Rakudo Star on 64-bit CentOS 6.5):

# Warning: This code does not work
use v6;

my $text = "A\nB\nC\n";

my $fh = $text;

while (my $line = $fh.get ) {
$line.say;
}
# Warning: Example of nonfunctional code


I get the error message:

No such method 'get' for invocant of type 'Str'
in block <unit> at string_fh.p6:8


It's not very surprising that Perl5's
open(my $fh, '<', \$text)
is not the same as Perl6's
my $fh = $text;
. So the question is: How does one create a virtual file handle from a string in Perl 6 like
open(my $fh, '<', \$str)
in Perl 5? Or is that something that has yet to be implemented?

UPDATE (writing to a filehandle in Perl 5)

Likewise, you can write to string filehandles in Perl 5:

use strict; use warnings;

my $text = "";
open(my $fh, '>', \$text);

print $fh "A";
print $fh "B";
print $fh "C";

print "My string is '$text'\n";


Outputs:

My string is 'ABC'


I haven't seen anything remotely similar in Perl 6, yet.

Answer

Reading

The idiomatic way to read line-by-line is the .lines method, which is available on both Str and IO::Handle.

It returns a lazy list which you can pass on to for, as in

my $text = "A\nB\nC\n";

for $text.lines -> $line {
     # do something with $line
}

Writing

my $scalar;
my $fh = IO::Handle.new but
         role {
             method print (*@stuff) { $scalar ~= @stuff };
             method print-nl        { $scalar ~= "\n" }
         };

$fh.say("OH HAI");
$fh.say("bai bai");

say $scalar
# OH HAI
# bai bai

(Adapted from #perl6, thanks to Carl Mäsak.)

More advanced cases

If you need a more sophisticated mechanism to fake file handles, there's IO::Capture::Simple and IO::String in the ecosystem.

For example:

use IO::Capture::Simple;
my $result;
capture_stdout_on($result);
say "Howdy there!";
say "Hai!";
capture_stdout_off();
say "Captured string:\n" ~$result;
Comments