3

I am battling with Moose these days, and I ran into the following problem. I create an object that has many required attributes on its creation. However, I wish to add attributes to it when a method is called. More specifically, I'd like to add the arguments for that method as a hash attribute. I'd like to do this so that subsequent calls to other methods know that the earlier method has already been called, with said parameters.

Example, but fictional code:

package Banana;

use Moose;

has ['peel', 'edible'] => (
  is  => 'ro',
  isa => 'Bool',
  required => 1,
);

has 'color' => (
  is  => 'ro',
  isa => 'Str',
  required => 1,
);

has 'grow_params' => (
  is  => 'ro',
  isa => 'HashRef',
);

sub grow {
  my ($self, $params) = @_;
  # params would be a hashref of method arguments
  $self->grow_params = $params;
  # Execute some code changing other, initial vars
}

This won't work, as the following error gets thrown:

Can't modify non-lvalue subroutine call of &Banana::grow_params

I've looked here on SO and on PerlMonks but I can't seem to find a general explanation of what the error means. Most answers simply re-write the original code and that's that. So what does the error mean, and can I accomplish what I'm trying to do? Or is this not the way to do it?

Bram Vanroy
  • 27,032
  • 24
  • 137
  • 239
  • Is there a reason you've made "grow_params" be read only? – Chris Turner Jul 24 '17 at 09:21
  • @ChrisTurner I wouldn't want people who use this package to be able to change the arguments of the method after they have called it. Doesn't seem necessary to me and could lead to issues, or am I missing something? – Bram Vanroy Jul 24 '17 at 09:23

2 Answers2

5

To explain a little more about what the error means...

A basic assignment statement looks like this:

$variable = 'value';

The operand to the left of the operator ($variable) is an "lvalue". The operand to the right of the operator ('value') is an "rvalue".

In the simple example above, the rvalue is a simple constant, but I hope you realise that it could also be another variable:

$variable = $some_other_variable;

or even the result of an expression:

$variable = 2 * $pi * $radius ** 2;

But you know (instinctively, at least) that the lvalue has to be a variable. You know that code like this makes no sense:

'value' = $variable;

Moose attribute accessors and mutators (the "getters" and "setters") are just subroutines. And subroutines are usually rvalues. This means that you can't (usually) assign an attribute like this:

$obj->attribute = 'value';

You would need to pass the new value to the method:

$obj->attribute('value');

However, there is a Moose extension called MooseX::LvalueAttribute which allows you to define lvalue mutator methods that work exactly how your original code expected them to. I really don't recommend it though, as it's not what maintenance programmers will expect to see in your code.

Dave Cross
  • 68,119
  • 3
  • 51
  • 97
3

Moose object attributes are hidden behind functions of the same name, so when you have $self->grow_params that's a function. Trying to assign a value to it won't work, you need to call it like a function.

$self->grow_params($params);

But even then, as you've currently defined grow_params as read only, you can't change it's value once the object has been created, even within it's own methods.

Chris Turner
  • 8,082
  • 1
  • 14
  • 18
  • Darn it, I knew this. I mean, that Moose automagically creates getters/setters. Silly me. I didn't know that if you set an attribute to RO you can't change it from within the Class's methods, though. Is there a way to restrict write-access to the current Class, and disallow writing from outside it? – Bram Vanroy Jul 24 '17 at 09:39
  • I just found this sub-module, [MooseX::Privacy](http://search.cpan.org/~franckc/MooseX-Privacy-0.05/lib/MooseX/Privacy.pm) which I'll give a go. – Bram Vanroy Jul 24 '17 at 09:46
  • 1
    If that doesn't work out, I think you might be able to write your own setter and check the stack trace with `caller()`? Not sure how Moose-y that is though as I'm new to Moose myself – Chris Turner Jul 24 '17 at 10:11