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

Is it possible to redefine string interpolation? Here's the context..

Let's say there is a class called 'SF' which is conceptually just a string plus a list of values. Concatenation on two SF objects works by concatenating the strings and concatenating the lists. For example:

$x = new SF("?", ["foo"]);
$y = new SF(",?", ["bar"]);
$x . $y is an SF object with string "?,?" and list ["foo", "bar"]
Ordinary strings have an obvious representation as SF objects by setting the list to the empty list, e.g.
  "blather" <-> new SF("blather", []);
Suppose that we have an SF-object $name. What I would like to happen is when perl encountered an interpolated string like:
  "Hello, $name"
that somehow the following transformations would occur:
  "Hello, $name" -> "Hello, " . $name
                 -> new SF("Hello", []) . $name
                 -> concatenation of two SF objects
In a sense, instead of stringifing $name, the other string would get SF-ified.

Any ideas of if this can be done in either perl5 or perl6?

Replies are listed 'Best First'.
Re: redefining string interpolation
by GrandFather (Saint) on Nov 28, 2009 at 01:04 UTC

    Something like this:

    package SF; use strict; use warnings; use overload '.' => 'catSF'; sub new { my ($class, $str, $list) = @_; return bless {str => $str, list => $list}, $class; } sub catSF { my ($lhs, $rhs) = @_; $lhs = SF->new ("$lhs") if defined $lhs && ! ref $lhs; $rhs = SF->new ("$rhs") if defined $rhs && ! ref $rhs; $lhs = SF->new ("$lhs") if defined $lhs && ! $lhs->isa ('SF'); $rhs = SF->new ("$rhs") if defined $rhs && ! $rhs->isa ('SF'); return SF->new ("$lhs->{str}$rhs->{str}", $lhs->{list}, $rhs->{lis +t}); } package main; my $name = SF->new ('GrandFather', [1, 2, 3]); my $catStr = "Name is $name"; print ref $catStr;

    Prints:

    SF

    True laziness is hard work
      return SF->new ("$lhs->{str}$rhs->{str}", $lhs->{list}, $rhs->{list});

      Shouldn't the merging of the lists look something like

      ... return SF->new ( "$lhs->{str}$rhs->{str}", [ map ref($_) eq 'ARRAY' ? @$_ : (), $lhs->{list}, $rhs->{list} ] );

      Another issue (which I don't have a solution for) is that what's LHS and RHS seems to vary with the circumstances of the concatenation, i.e. while this

      my $name1 = SF->new ('GrandFather', [1, 2, 3]); my $name2 = SF->new (' and Almut', [4, 5, 6]); my $catStr = $name1 . $name2; use Data::Dumper; print Dumper $catStr;

      produces (as expected)

      $VAR1 = bless( { 'str' => 'GrandFather and Almut', 'list' => [ 1, 2, 3, 4, 5, 6 ] }, 'SF' );

      "Name is $name1" (or "Name is " . $name1)  doesn't :

      $VAR1 = bless( { 'str' => 'GrandFatherName is ', 'list' => [ 1, 2, 3 ] }, 'SF' );

      (note that "Name is " is appended to "GrandFather", not the other way round)

        Changing the SF constructor to:

        sub new { my ($class, $str, @lists) = @_; my @list = map {'ARRAY' eq ref $_ ? @$_ : $_} @lists; return bless {str => $str, list => \@list}, $class; }

        fixes the list concatenation issue. The ordering issue is fixed by changing the first few lines of catSF to:

        sub catSF { my ($lhs, $rhs, $inv) = @_; ($lhs, $rhs) = ($rhs, $lhs) if $inv;

        True laziness is hard work