in reply to optimization - exists should return a reference

What if the element doesn't exist?

In a naive implementation, returning undef, zero, or an empty string from exists will fail dereference, and it would happen at runtime. A reference value that evaluates false, yet returns undef when dereferenced, seems to be required.

That seems to need a tricky sort of special-casing that would affect all dereferences.

Update: BrowserUK points out that dereferencing the return of exists yields undef. That does not pass use strict 'refs';, however. exists currently returns the empty string as false, generating Can't use string ("") as a SCALAR ref while "strict refs" in use at -e line 1..

Update 2: To clarify my point, this idea leads to:

my %foo; my $bar = ${exists $foo{'bar'}};
giving assignment to the value without autovivification. The need to provide a reference which is false in bool context (for campatibility) is the basis for my objection.

After Compline,
Zaxo

Replies are listed 'Best First'.
Re: Re: optimization - exists should return a reference
by BrowserUk (Patriarch) on Jan 15, 2003 at 13:30 UTC

    I don't quite follow you?

    print $$ref if my $ref = exists %hash{something);

    Update Above replaced after Abigail-II's post below.

    print $$_ if $_ = exists $hash{something);

    Regardless of what false value is returned by exists if the thing doesn't exist, the if will be false and so $ref will never be dereferenced.

    Or am I missing something as usual?


    Examine what is said, not who speaks.

    The 7th Rule of perl club is -- pearl clubs are easily damaged. Use a diamond club instead.

      That won't do what you think it does. Remove the my for it to work.

      Abigail

        Good point. I'm always doing that in real code too. I substituted $_ for the purposes of the example, but I realise that may not be useful in many situations.


        Examine what is said, not who speaks.

        The 7th Rule of perl club is -- pearl clubs are easily damaged. Use a diamond club instead.

Re: Re: optimization - exists should return a reference
by BrowserUk (Patriarch) on Jan 15, 2003 at 17:50 UTC

    So what your saying is that because someone might do

    print ${exists $foo{'bar'};

    instead of

    print $$_ if $_ = exists $foo{'bar'};

    and get a runtime error something like Use of uninitialized value in scalar dereference ...

    you would prevent anyone from benefiting from the advantages of using it properly?

    Given that in the example above, the user has at least acknowledge that $foo{'bar'} may not exist by the very act of using exists, then that's seems analogous to, and a much less easy mistake to make than doing

    print $foo{'bar'};

    instead of

    print $foo{'bar'} if exists $foo{'bar'}

    currently, which also results in a runtime warning.

    Isn't it kind of against the spirit of Perl's we ask you to read the warning signs rather than enforce them with a shotgun approach?


    Examine what is said, not who speaks.

    The 7th Rule of perl club is -- pearl clubs are easily damaged. Use a diamond club instead.

      Yes, that's exactly what I'm saying.

      If exists is to return a reference, it should always return a reference. Code exists which says exists( $foo{'bar}) ? 'Exists' : 'Does not exist'. That code will break if exists returns a reference, because references always evaluate true.

      If what we get from exists is a reference, we should be able to treat is like any other reference. You seem to want to be able to assign from it and dereference the copy, but deny the ability to dereferencee it directly.

      Just how is that supposed to work?

      After Compline,
      Zaxo

        Sorry Zaxo. I know I'm being thick here, but if the entity being testing for existance doesn't exist, exists can't return a reference to it. And if it did return a "false reference", you wouldn't be able to do anything with it except test it for being false, so it should just return false, whether that is undef, 0, or '' makes no difference. As long as it returns false, then existing code like

        print exists( $foo{'bar'} ) ? 'Exists' : 'Doesn't exist';

        doesn't break.

        I'm sure I missing the salient point here, but I can't see where.


        Examine what is said, not who speaks.

        The 7th Rule of perl club is -- pearl clubs are easily damaged. Use a diamond club instead.

        Right now, we can only use exists as a boolean. The suggestion is to extend it so that when the value returned is true that value is itself useful.

        Assume for a minute that the hypothetical reference-that-evaluates-to-false-in-boolean-context actually could be returned... what use would it be to dereference it anyway?

        If you really wanted something like that, you could forge it yourself:

        my $bar = ${exists($foo{bar}) || \undef};

        -sauoq
        "My two cents aren't worth a dime.";
        

        "If exists is to return a reference, it should always return a reference."

        I don't know about that. Let's say we're looking at exists($foo{'bar'}). So, if $foo{'bar'} exists, exists returns a reference to it. Otherwise, it returns undef or an empty string, or 0 or whatever the hell. Something that's false in a boolean context. One potential problem, as you point out, is if someone tries to dereference the result without checking to see if it's true first. (I'll assume for the time being that it's out of the question to change the way references evaluate in boolean context :o)

        I would say that checking for the existance of the value returned by exists is pretty natural. For instance the example put forward by BrowserUK:

        use strict 'refs'; print ${exists $foo{'bar'}};

        would yield Can't use an undefined value as a SCALAR reference or something of the like. This would be an example of bad coding: using a value without being sure that there is a value. It's really no different from: The same mistake is made (and the same error potentially generated) as if the following were written:

        use strict 'refs'; print ${$foo{'bar'}};

        This would yield the same error. Whereas one 'right' way to do the same thing would be:
        One 'right' way to use the reference from the proposed exists would be:

        use strict 'refs'; print ${exists $foo{'bar'} || \"" };

        ...perhaps substituting a reference to a default value or somesuch for the \"". What I'm saying is that this puts responsibility on the programmer to make sure she doesn't try to dereference anything undefined or whatever. If she wants to be able to do stuff like print ${exists $foo{'bar'}} she's not going to use strict. For the rest of us, we get added functionality for exists that doesn't break existing code.

        Update: The crux, in my mind, of why print ${exists $foo{'bar'}} doesn't make sense just finally came to me in actual words: it's like writing print ( if ($foo{'bar'}) ), or print ( $foo > $bar ). It's a boolean test, which is not designed to stand alone. Conceptually, exists exists to choose between two options.

        Update: Aristotle pointed out some ambiguity in the way I phrased some stuff. Also, I noticed that in my first paragraph I had written exists $foo instead of exists $foo{'bar'}.


        LAI
        :eof
Re^2: optimization - exists should return a reference
by Aristotle (Chancellor) on Jan 15, 2003 at 23:19 UTC
    Sorry, I don't understand. Dereferencing in that way would be a "useless use of exists". To make it not break, you'd simply write: my $bar = ${exists $foo{'bar'} or \undef }; But that is exactly the same as my $bar = $foo{'bar'}; Neither of them vivifies anything. Autovivification takes places only for multilevel deref: my $bar = $foo{'bar'}{'baz'}; Now there will be a bar key in %foo indexing an empty hashref, even if none was there before. A baz key, again, will not be vivified into this new %{$foo{'bar'}} hash. Note that, again, the same will happen if you try this: my $bar = ${exist $foo{'bar'}{'baz'} or \undef}; But the new exists semantic could be used to simplify writing a truly non-vivifying multilevel deref:
    my $qux = \%foo; $qux = exists $qux->{$_} or (undef $qux, last) for qw(bar baz quux qux);

    Makeshifts last the longest.