Flash
Flash

Reputation: 3021

Can't call method state - error

I am pretty new to Perl and am trying my hand at solving a few exercise problems from the Llama book. The question was to write a sub-routine that greets a person as welcome if he is the first or else tell him the name of the previous person who was there. My code looked like:

#! perl

# This is the greeter problem .

sub greetPerson{
    state $previous = undef ;

    if($previous eq undef){
     print " Welcome ... u r the first \n";
    }
    else{
     print " Hi ... $previous was already here \n";
    }

    $previous = $_;
    return 0; 
}

@code = ("ravi" , "teja" , "chandu");
greetPerson($code[0]);
greetPerson($code[1]);
greetPerson($code[2]);

I get an error saying

 "Can't call method state on undefined value in Line 6.  "

Please help me as to what this means and why it is coming and how to solve it .. ? I understand that "state" is synonymous with "static" in other languages (C / Java). Am I wrong?

Upvotes: 2

Views: 2643

Answers (2)

stevenl
stevenl

Reputation: 6798

state is a fairly recent feature (since Perl 5.10) that is not enabled by default. To enable it:

use feature 'state';

Using state is kinda equivalent to having static variables in say Java. Another way of doing it without state is to declare $previous with a simple my outside of greetPerson, which means it is scoped within your entire script.

As an aside, you should also include these lines in all your scripts to save yourself a lot of trouble down the track:

use strict;
use warnings;

Also, in greetPerson you need to get your arguments, which you can get from @_. E.g.

my ($name) = @_; # or
my $name = $_[0];

Upvotes: 9

Jonathan Leffler
Jonathan Leffler

Reputation: 754410

Yes; you have mistaken things (but so have I). AFAIK, state has no specific meaning in Perl (but someone else says it is a new feature that has to be enabled).


Hmmm...Perl 5.14.2 has a function state. It was introduced in 5.10.0; I've checked a 5.10.0 version on my machine and it was there, even though I was unaware of it. DavidO helpfully confirms that state was not in the 5.8 series of Perl versions; the change was added in 5.10.0.

So, you need a modern enough version of Perl and you need to enable it with:

use feature 'state';

When the state feature is not enabled, it looks like a method call, and since the method isn't defined, you are getting the error you see. Of course, it is a method call even when it is enabled; or, at least, it is both listed as and described as a function, but it is a built-in function with special semantics.

Just in case you hadn't realized, one of the great things about StackOverflow is that you learn by answering questions.

You might be looking for my (or our or local, but those are unlikely here).

However, a variable in a subroutine will vanish when the subroutine exits, so even my isn't quite what you're after.

In the absence of state, you probably would use a global variable (without any initialization so that on the first pass its value is undef):

my $previous;

outside the function, and then you simply reference that in the function:

sub greetPerson{

    if ($previous eq undef){
     print " Welcome ... u r the first \n";
    }
    else{
     print " Hi ... $previous was already here \n";
    }

    $previous = $_[0];
    return; 
}

This also fixes the problem that $_ is not related to the arguments passed to the function. You might be better off writing:

sub greetPerson{
    my($who) = @_;
    if ($previous eq undef){
        print " Welcome ... u r the first \n";
    }
    else{
        print " Hi ... $previous was already here \n";
    }

    $previous = $who;
    return; 
}

You should also use use warnings; and use strict; in general. Experts use them to make sure they haven't made silly mistakes. Novices should do the same. It does mean you have to declare all your variables.

One of the nice things about state is that it allows you to hide a variable that would otherwise have to be global (or hidden at the module level if you were working with modules) inside a function where it really belongs. So, it is a useful feature to know about.


From the comments

I am passing a scalar (@code[0]). Then, why do I need to use @_[0] there? Is $_ not the one? But somehow, @_[0] works and not $_ ... Can you please explain?

The full story is complex, but the way Perl passes arguments to a function is as an array. Inside the function, the arguments are available in @_. The variable $_ is unrelated to @_ (except that the last character of its name is the same).

Note: I've modified my first example to reference $_[0] instead of @_[0]. That is accessing the zeroth element of the @_ array, and still nothing much to do with $_ the scalar. This is one area where Perl does cause confusion. (If @_[0] really worked, then either I got lucky, or some of the Perl 6 ideas about sigils are creeping into Perl 5.1x.)

Over time, you will get used to the notation:

sub some_name_or_other
{
    my($arg1, $name2, $obj3, $ref4, $hash5, @the_rest) = @_;
    ...
}

This assigns the first 5 arguments to the scalar variables, and stuffs anything left over in the local array @the_rest. If you call it with too few arguments, you get undef values. The parentheses areound the my(...) list are crucial. They provide list context and make it work. Omit the parentheses, and things will go wrong. However, list context is a bigger subject than I want to discuss in detail here; please look in the Llama book (or the Camel book, or online) for the details.

If you are going to reference the argument exactly once, promising not to change the value, you can use $_[0] notation; if you're going to use it more often, use a my variable. Preferably, use the my variable anyway; it is better for comprehensibility.

Upvotes: 2

Related Questions