2
#!/usr/bin/perl

sub f { {
  a => 1,
  b => 2
} }

sub g { {
  %{f()},
  c => 3,
  d => 4,
} }

use Data::Dumper;
print Dumper g();

The above code outputs

$VAR1 = 'a';
$VAR2 = 1;
$VAR3 = 'b';
$VAR4 = 2;
$VAR5 = 'c';
$VAR6 = 3;
$VAR7 = 'd';
$VAR8 = 4;

despite in my understanding it should output

$VAR1 = {
          'a' => 1,
          'c' => 3,
          'b' => 2,
          'd' => 4
        };

What is my misunderstanding?

porton
  • 5,214
  • 11
  • 47
  • 95
  • I was going to write an answer to the effect that it's a context problem. But for all it's a bit icky style, `f()` does return a hashref, but `g()` returns a list. I think it must be to do with the unpacking `%{f()]` causing a return in a list context. – Sobrique Jul 31 '18 at 12:45
  • 1
    @hoffmeister: You haven't explained why `Data::Dumper` works with one function but not the other. This is clear, because `%{ f() }` works fine, – Borodin Jul 31 '18 at 12:59
  • 1
    Quasi-duplicate of [Difference between returning +{} or {} in perl from a function, and return ref or value](https://stackoverflow.com/q/42100478/589924) – ikegami Jul 31 '18 at 14:32
  • @ikegami , you're right, i did not realise that perl sees it as a code block, not a reference. However, i do not think you're answer makes it clear from a sub-routine you can either return a single variable or a list. So here when we call g() Data::Dumper it prints a list. I've only known data::dumper to print a hash if you pass a reference, hence why we need to make it a hash reference somehow. let me know if you want me to delete my initial comment – hoffmeister Jul 31 '18 at 15:26
  • 1
    @hoffmeister, I don't see the point of explaining that subs can return multiple scalars (and neither did Borodin, for that matter). The problem is that `{ }` isn't doing what it's expected to be doing. I explained why that's the case, and how to fix it. – ikegami Jul 31 '18 at 16:04

2 Answers2

9

The problem is that a pair of braces is ambiguous in Perl, and may be either a block or an anonymous hash

Because of the contents of the hash in your g (please use better names) perl has assumed that you are writing a code block, which is just a list of scalar values

Make it more explicit like this and your code will function as you expect

use strict;
use warnings 'all';

sub foo {
    {
        a => 1,
        b => 2,
    }
}

sub bar {
    my $href = {
        %{ foo() },
        c => 3,
        d => 4,
    }
}

use Data::Dump;
dd bar();

output

{ a => 1, b => 2, c => 3, d => 4 }
Borodin
  • 126,100
  • 9
  • 70
  • 144
  • 1
    Are `foo/bar` really better function names than `f/g` ? I mean, none are a good name for a real code, but for a minimal example, `f` and `g` seem fine to me.. – Dada Jul 31 '18 at 21:36
  • 1
    @Dada: There is a reason why the community settled on `foo`, `bar`, and `baz` instead of `f`, `g`, and `h`. Shorter names will always be preferred without another overriding reason (which is why the more common Perl language words are also generally the shorter ones). The problem is that single-letter identifiers tend to "disappear" into the the surrounding symbols, so `%{foo()}` is much clearer than `%{f()}`. – Borodin Aug 01 '18 at 09:51
7

The Perl language has ambiguities. Take for example

sub some_sub {
   {  }     # Is this a hash constructor or a block?
}

{ } is valid syntax for a block ("bare loop").
{ } is valid syntax for a hash constructor.
And both are allowed as a statement!

So Perl has to guess. Perl usually guesses correctly, but not always. In your case, it guessed correctly for f(), but not for g().

To fix this, you can give Perl "hints". Unary-+ can be used to do this. Unary-+ is a completely transparent operator; it does nothing at all. However, it must be followed by an expression (not a statement). { } has only one possible meaning as an expression.

+{ }   # Must be a hash constructor.

Similarly, you can trick Perl to guess the other way.

{; }   # Perl looks ahead, and sees that this must be a block.

So in this case, you could use

sub g { +{
  %{f()},
  c => 3,
  d => 4,
} }

or

sub g { return {
  %{f()},
  c => 3,
  d => 4,
} }

(return must also be followed by an expression if anything.)

ikegami
  • 367,544
  • 15
  • 269
  • 518