Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things
 
PerlMonks  

Re: Re: Re: What should be returned in scalar context? (best practice)

by sauoq (Abbot)
on Dec 21, 2003 at 19:16 UTC ( #316229=note: print w/ replies, xml ) Need Help??


in reply to Re: Re: What should be returned in scalar context? (best practice)
in thread What should be returned in scalar context?

Document that it returns a list. It's not possible to return an array.

No. I would (will and do) document that it returns an array. What you are saying might be true from an internals standpoint, but that's irrelevant. Read the code: return @ret; . . . it says "return" and is followed by an array. More importantly, it behaves like an array. It doesn't behave like a list literal (a slice does, however, as ysth points out.) It certainly doesn't behave like an array reference. Documenting that it returns an array is the only clear way to describe its behavior.

If you document that some functions returns an array, the user can only guess: is it a list or an array reference?

When a funtion returns an array reference, document that it returns an array reference. "Array" and "array reference" are not synonyms. The user doesn't have to guess! He just has to read.

If you sometimes describe a reference to an array as "an array", then you are being sloppy and you should stop.

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


Comment on Re: Re: Re: What should be returned in scalar context? (best practice)
Download Code
Re: Re: Re: Re: What should be returned in scalar context? (best practice)
by Juerd (Abbot) on Dec 21, 2003 at 19:38 UTC

    Read the code: return @ret; . . . it says "return" and is followed by an array.

    print @foo; does not print an array, it prints a list.

    More importantly, it behaves like an array.

    Only in two ways. It returns a number of elements in scalar context and a list of elements in list context. That's where the comparison ends.

    You can't splice what your function returns, push onto it, shift from it, unshift onto it, pop from it, reference it as an array, tie it, assign to it or undef it.

    It is not an array. Don't call it an array, because it simply is not an array. You say it behaves like an array, but it does not. The very things that make an array an array do not apply. Just some context sensitivity, but that does not make your list or number an array.

    If you don't want to call it a list because in scalar context it returns a number of elements, specify both behaviours in your documentation. That's what perl's documentation also does. See grep, keys, split, etcetera.

    "Returns a list" is wrong when the sub is called in scalar context. "Returns an array" is wrong in every possible context. Note that "array context" does exist and your sub cannot be used in it.

    Only an lvalue sub can return an array, but that cannot be done using the return keyword.

    It doesn't behave like a list literal

    Indeed. It does not behave like a list *literal*. But in list context, it does behave like a list, in every single way. It's important to know that there are two kinds of lists, and that list literals are special with syntax; other than that, they're just lists.

    Documenting that it returns an array is the only clear way to describe its behavior.

    No, no, no, no, NO! It's the most vague way to describe its behaviour. Don't call it clear, because it is the very opposite of clear! If you want to be absolutely clear, you must specify the difference in behaviour for every context, or modify the sub so that it really does behave like an array, or like a list.

    "Array" and "array reference" are not synonyms.

    Correct. But why do you think that "list" and "array" are synonyms? They're not. My point is not that "returns an array" would imply it being a reference. My point is that it's entirely unclear whether it returns a list or a reference to an array. It's not returning an array, that's for sure (since you use the return keyword, we're not talking about an lvalue sub here).

    If you sometimes describe a reference to an array as "an array", then you are being sloppy and you should stop.

    I could not agree more.

    I'd like to add this one:
    If you sometimes describe a list as "an array", then you are being sloppy and you should stop.

    Juerd # { site => 'juerd.nl', plp_site => 'plp.juerd.nl', do_not_use => 'spamtrap' }

      Juerd, I am perfectly aware that functions always return a list. Does that mean I'm going to stop documenting that a function like sub f { return 0 } "returns a scalar?"

      No.

      Somehow I doubt you'd take issue with that even though it isn't technically true. That function is, afterall, returning a list. The list simply has one element. Your argument, if applied evenly, would require the abolishment of the phrase "returns a scalar" as well. Fortunately, we won't have to do that, because your position is completely untenable.

      By saying your function "returns a scalar" or "returns an array" or "returns a list" you are able to describe exactly how code the is written without trying to describe how Perl works.

      The reason it makes sense to say those things is that context is imposed before the value is returned. When you return @ret; the list returned is composed of elements generated by evaluating @ret in the required context. If it is in scalar context, you get the number of elements of @ret. If it is in list context, you get all of the elements of @ret. Likewise, if you return (33, 42); the list returned is created by evaluating (33, 42) in scalar or list context as necessary.

      In other words, there is always a disconnect between what you return and what Perl returns. If you return (33, 42); and your function is called in scalar context, the function returns (42).

      I document what I do. Perl is already well-documented.

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

        Nevermind. You're not getting it. If you document only what your code does, you might as well just duplicate the source.

        You say that my argument is "overly academic with no practical basis". Perhaps it is. It's not something I have considered. I'm just someone who hates documentation that is wrong and vague where it could be correct and clear if the author cared.

        Technically and linguistically, you're wrong if you say that a Perl non-lvalue subroutine returns an array. Write one sentence more and your documentation is correct and clear.

        Instead of Returns an array of foos. just write Returns a list of foos. (In scalar context, returns the number of foos.) and stop making people guess what you mean.

        If you insist on redefining the word 'array', go ahead. But don't forget to document your meaning of the word.

        I am perfectly aware that functions always return a list. Does that mean I'm going to stop documenting that a function like sub f { return 0 } "returns a scalar?" Somehow I doubt you'd take issue with that even though it isn't technically true. That function is, afterall, returning a list. The list simply has one element.

        No, it is perfectly acceptable to call a one-element list a scalar in this context. However, it is not very customary to mention that the single value you return is a scalar. Usually, we talk about "a number" or "a string". "Returns true", "returns the integer portion", "returns a string", "returns the number of". In fact, no function in perlfunc is documented to return a scalar.

        However, it is customary to indicate that a collection of numbers or strings is returned by using the word "list", because such a collection is called a "list" in Perl jargon.

        By the way, why do you think my argument has no practical basis? That's nonsense. Consistency with existing jargon and documentation is very practical.

        Your argument, if applied evenly, would require the abolishment of the phrase "returns a scalar" as well.

        Saying that a function returns a scalar isn't even wrong, even if the function returns 15 scalars, it still also returns a scalar. It would be misleading, though, so I'd avoid it. But as I said, it is not common to say that a function returns any scalar at all. We usually describe what the scalars are, since we assume every Perl coder knows that normal (non-lvalue) subs can only return scalars.

        Fortunately, we won't have to do that, because your position is completely untenable.

        I'm wasting my time. You're even more stubborn than I am. Even when presented with facts and prior art, you refuse to see that what you do is different from what others have agreed on.

        By saying your function "returns a scalar" or "returns an array" or "returns a list" you are able to describe exactly how code the is written without trying to describe how Perl works.

        A sub returns a foo or a list of bars. You don't document that a function returns a scalar or just a list. But if you do, that's acceptable. Documenting that a function returns an array is *not acceptable* if it does not return an array. Is that so hard? Don't say your function returns 2 if does not return 2. And don't say your function returns 1+2 if it has return 1+2 in it. It returns 3. Stick to the facts and keep it simple (but never make it inaccurate). This is technical documentation, not prose.

        The reason it makes sense to say those things is that context is imposed before the value is returned.

        Actually, it depends on how you look at it. Context is propagated to every single return statement, and to the last statement of the sub.

        When you return @ret; the list returned is composed of elements generated by evaluating @ret in the required context. If it is in scalar context, you get the number of elements of @ret. If it is in list context, you get all of the elements of @ret. Likewise, if you return (33, 42); the list returned is created by evaluating (33, 42) in scalar or list context as necessary.

        That is correct. But even if you have return @foo, @foo, the array, is never returned. It cannot be referenced or mutated. The thing that is returned is not an array. The function does not return an array, ever.

        It's very simple. You can call something an array if it starts with @. Because lvalue subs are reasonably new, some work-around syntax is needed to actually use an lvalue sub as an array: @{\foo} instead of just foo, and that too proves my point that only if it starts with @, it is an array.

        Note that I'm not saying that everything that does start with @ is an array. It could be a slice.

        This rule also means that in $foo[0], $foo is not the array. @foo is the array. Another typical aspect of arrays is that you can use them in array context, like the context imposed on the first argument of push. If you can't push @{\foo()}, "bar";, foo did not return a regular array. If it did return an array and it doesn't work, that is because it was made readonly or magical (tied).

        Obey gravity, it's the law.

        In other words, there is always a disconnect between what you return and what Perl returns.

        Better described as "there can be a disconnect between syntax and semantics". And API documentation is about explaining what happens, not about how you make that happen.

        $i++; # increment $i # silly.
        Comments should describe the "why";
        Documentation should describe the "what";
        Code describes the "how".

        Perl is already well-documented.

        And it is time to document your own code well too. Perl set the example, now all you have to do is follow that example. Perl is well known for it's high quality documentation, but wouldn't have been if perlfunc did crazy things like tell people that some function returns an array.

        If keys returned an array, not only would it return a list of elements in list context and a number of elements in scalar context, it would also be spliceable. And pushable, and shiftable. And the best of all: it would be referable. Yes, that is silly. But that is what returning an array is.

        Juerd # { site => 'juerd.nl', plp_site => 'plp.juerd.nl', do_not_use => 'spamtrap' }

        This is my last node in this discussion. I am unable to convince you. You would defy gravity if you could. I just hope other people who read this thread did learn from it, and don't say that functions return arrays.

Re: Re: Re: Re: What should be returned in scalar context? (best practice)
by !1 (Hermit) on Dec 21, 2003 at 23:59 UTC
    I would (will and do) document that it returns an array...When a funtion returns an array reference, document that it returns an array reference.

    Ummm. Perhaps it just has to do with the way I've been taught to document my code but documenting that a subroutine returns an array is very ambiguous especially considering the fact that you can have a subroutine detect context and return the contents of the array for list context and an arrayref in scalar context (this is the behavior I actually expect when the documentation says array because part of my job involves using mod_perl where you need to watch memory consumption). Of course, you could also have it return the number of elements in scalar context. However, just saying that it returns an array can most certainly be a cause for confusion. Since the purpose of documentation is to make certain that the reader clearly understands how to use your module, avoid using "returns an array".

      documenting that a subroutine returns an array is very ambiguous especially considering the fact that you can have a subroutine detect context and return the contents of the array for list context and an arrayref in scalar context (this is the behavior I actually expect when the documentation says array

      Well, if the function returns an array reference in scalar context, the documentation should state that it "returns an array reference in scalar context." Simple as that. An "array" is not an "array reference" and to expect it to become one in scalar context is ridiculous.

      Just take these two functions:

      sub f1 { return my @x = (33, 42) } sub f2 { return (33, 42) }
      To document that the first returns a list and the second returns an array is perfectly acceptable (preferable, imho.) When you see f1() in the code, you know how it is going to act. Number of elements: my $nelts = f1(); ... First element: my ($first) = f1(); ... similarly, when you see f2() in the code, you can know how it will act. Last element: my $last = f2(); ... First element: my ($first) = f2(); (This is exactly why this discussion came up in the first place. Read the first node in this thread to orient yourself.)

      Juerd's mumblings (or ravings) about Perl builtins are quite irrelevant. Besides the fact that every single one of them is a special case, they aren't implemented in Perl to begin with; they are implemented in C.

      The phrase "returns an array" is a very nice way of explaining exactly what you mean without addressing every context separately. I won't avoid it.

      Update: Examples flipped. Thanks jarich for pointing that out. (I actually flipped my description, because I wanted to talk about list values first, and forgot to flip my examples.

      -sauoq
      "My two cents aren't worth a dime.";
      
        "returns an array" would be confusing to me, since it's not possible. Well, except maybe in an "lvalue" context.

        An array is a container, holding a list. You can't return containers. Only values. Scalar value is to scalar variable what list is to array. You can no more return a scalar variable than you can return an array.

        Put another way, you can't take a reference to a list, only to an array. And yet you can't take a reference to the return value of a subroutine. You have to construct an anonymous array (a new container) to hold the value.

        These are not just implementation issues. These are user-visible concepts, and affect your behavior if you have the proper model.

        Your casual conversation may make sense to you, but you'll confuse your reader. And the point of communication is to communicate for the reader, not just for your own notes.

        -- Randal L. Schwartz, Perl hacker
        Be sure to read my standard disclaimer if this is a reply.

        Now let me try this:

        sub f1 { return (33, 42) } sub f2 { return my @x = (33, 42) } my $nelts = f1(); # apparently returns number of elements (2) my ($first) = f1(); # apparently returns first element (33) my $last = f2(); # apparently returns last element (42) my ($first2) = f2(); # apparently returns first element (33) print "nelts: $nelts (expecting 2)\n"; print "first: $first (expecting 33)\n"; print "last: $last (expecing 42)\n"; print "first2: $first2 (expecting 33)\n";

        Let's run that shall we?

        # Output: nelts: 42 (expecting 2) first: 33 (expecting 33) last: 2 (expecing 42) first2: 33 (expecting 33)

        Hmm. So your suggestion that you can know how it will act isn't even quite true for yourself. But only because you got your examples backwards.

        It's interesting though, because I never really bothered noticing a different between the two return options. What you're saying is that if you give return an array then the returned value gives you the number of elements in the list when taken in scalar context. Whereas if you give return a list then the return value gives the final element of the list in scalar context.

        An unfortunate inconsistency and one which probably causes many subtle bugs.

        To document that the first returns a list and the second returns an array is perfectly acceptable

        I understand why you're arguing that there is a distinction between these two cases and I can guess why you feel its better to describe the second as returning an array. However I do agree that the statement is technically incorrect. The subroutine may be returning as an array but what you get back on the other side is a list until you copy it into an array. And you want it to be a list too, as there's very little value of even thinking of it being an array except for the fact that you can take it in scalar context to get the number of elements.

        Since the issue is on semantics and your attempt to denote a difference between the two ways of returning a list, I cannot recommend a better choice for how you should document this. I would say that the subroutine returns a list or the number of elements in that list in scalar context, rather than saying it returns an array. But I can understand, even if I don't agree, your reasons for your choice of words.

        I'm now going to have to take especial care in future, when calling subroutines which return lists in scalar context. Will I be getting the final value of the length of the list, because I suspect I usually assume the latter.

        jarich

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://316229]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others examining the Monastery: (13)
As of 2014-07-31 20:33 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    My favorite superfluous repetitious redundant duplicative phrase is:









    Results (253 votes), past polls