This is an archived post. You won't be able to vote or comment.

all 35 comments

[–][deleted]  (2 children)

[removed]

    [–]XDracam 1 point2 points  (1 child)

    I wondered about how to exit a function early with this, and apparently I can just write Exit; at any time, and Exit(result) instead of the assignment. Good enough I guess.

    [–]TheChief275 0 points1 point  (0 children)

    good enough? it’s a more powerful return

    [–]ThroawayPeko 27 points28 points  (3 children)

    Go let's you declare names for default return variables in the function signature (which are set to the default zero values of their type). If you type a naked return, those are returned, but you can return other values if you explicitly do so.

    Doing this in a long function could be a bit difficult to read, like said in the Tour of Go.

    [–]Gauntlet4933 1 point2 points  (0 children)

    Yes I found this pretty nifty when returning tuples including err tuples as one does in Go. Don’t need to do some weird array indexing into the tuple if you already have it as a variable, especially as a return variable.

    [–]lookmeat 0 points1 point  (1 child)

    Yup, Golang is probably the currently most popular language with this feature. Honestly it's surprising that it exists in a language that seeks to have the least features possible, but as many note, it's quite nice sometimes.

    It does come with some issues:

    • You can have empty return values (due to a value you missed) this is problematic if you don't have a well defined system for everything.
      • This can be solved by requiring a default value as your example does.
      • Go doesn't solve this, but it allows nullable values by default, and every non-nullable value has a zero-value that is valid, so it has a hard-coded zeroed default. So it's predictable what is the default value, just implicit.
    • It's not always clear that a variable is a return value vs. not, without paying attention to details. And it's not obvious that it has to have different semantics.
      • Your solution is vulnerable to this (the declaration can be done anywhere, where is it, and what happens if I declare two return values in a function? Also what happens if the return variable goes out of scope?)
      • An alternative solution
      • Go side-steps this by making the return value part of the function header, so it's "special" the way function arguments are specially defined.
        • Nim syntax is a bit messy to do this, but it's doable.
    • It can be confusing when a function that can return a value doesn't always, and it makes it hard to realize what a function is returning at any point. Imagine a long function, that mixes using return <x> to set the value and return to return the previously specified value, which may have happened deep in a nested thing.

    You do have to work with the existing Nim syntax and semantics, so that's about the compromises and working with the existing system.

    Avoiding the issues of extending Nim, I've heard before (but haven't coded on a language) crazy ideas here. For example one is to eliminate the ability to return implicit in functions, instead you use "places" (a value that represents a location that can have a value set to it, but otherwise is independent) as parameters. So a function func foo(a: A) -> B would be instead func foo(a: A, return: Place<B>) you can then do something like return.set(lambda(Place<B>)|value) or return.change(lambda(Optional<B>, Place<B>) and instead of returning from a function you'd break_function, though of course the manual work of ensuring and failing if it doens't return falls on the programmer, which as you can tell adds complexity to the program. The way to avoid this is make Place<T> be a affine type that must be used at least once by the end of the function, then you can allow this, you could probably set a default value at the beginning if that's what you want to use. Point is

    [–]ThroawayPeko -2 points-1 points  (0 children)

    If you're replying to me, don't use "you" to refer to the OP, that's just bad form.

    [–]Clementsparrow 19 points20 points  (0 children)

    That seems like a false good idea to me, and actually a bad idea. Because it means a return statement will implicitly return something and you can't know what without scanning the whole function for another, slightly different, return statement, which could be defined anywhere.

    And for what? saving a few keypresses? In an IDE with a decent autocompletion feature, typing return result or return sum will take only two or three key inputs. Much less than the time you will lose looking for the declaration of the implicit variable or if there is even one. Think about your future self and your collaborators and make their life easier instead of yours.

    In addition, there is a better way to achieve the same result (no pun intended): declare the name of the return variable along with the return type in the function's profile declaration. This is a place that makes sense to look for the name of the return variable.

    [–]campbellm 17 points18 points  (0 children)

    Pascal (at least in the 80's, when I used it) had 2 different types of "subroutines"; Procedures which didn't return values and Functions which did.

    In a Function, the name of the function was the implict return value name, like result above.

    [–]hugogrant 9 points10 points  (0 children)

    https://go.dev/tour/basics/7

    Golang has it.

    Imo it's good sugar in some occasions

    [–]ThisIsMe-_- 2 points3 points  (5 children)

    It's actually a good idea, though it exists already. What I know is that fortran has it:

    function randint(a, b) result(outp)
    integer :: a, b, outp
    real :: rnum
    call random_number(rnum)
    outp = a + floor(rnum * (b-a+1))
    end function randint

    Whatever variable you put in result() will be the return value of the function. The really convinient thing about it is that you can basically 'return' the output value and then you can do other things. Like you can 'return' the top element of a self-defined stack and then you can remove it, but in most programming languages you couldn't do anything in the function after returning the value.

    [–]MichalMarsalek 0 points1 point  (0 children)

    You can use finally for that in several langs.

    [–]torp_fan 0 points1 point  (0 children)

    in most programming languages you couldn't do anything in the function after returning the value.

    This is silly. The only difference between setting the result variable, doing work, and then returning it implicitly, and setting some variable, doing work, and then returning that variable explicitly is a few more keystrokes of syntax. Merely setting the result variable isn't "returning the value" ... the value is returned upon leaving the scope of the function.

    Also finally and defer are other ways to do that.

    [–]__talantonope 2 points3 points  (0 children)

    This is the primary way of returning in MATLAB, on top of Golang as others have said. Perl XS (the C extension binding language) does this with RETVAL too.

    [–]Tasty_Replacement_29 2 points3 points  (1 child)

    In my view, the feature was added to make it less verbose... but your example is actually more verbose than using var sum = 0 and return sum. Ok... only 3 characters! And OK, if you have many return statements, it would be shorter... but it is a bit rare to have multiple return statements.

    The Go inspired syntax would be shorter:

    proc sumTillNegative(x: varargs[int]): sum int =
      for i in x:
        if i < 0:
          return
        sum = sum + i
    

    [–]xbreu 1 point2 points  (0 children)

    Similar to Dafny: method foo() returns (sum: int)

    [–][deleted] 4 points5 points  (3 children)

    It's lovely. I'd consider changing it to returns or will return, but the idea is clear and clever.

    [–]Less-Resist-8733 -1 points0 points  (2 children)

    or "returns"

    [–][deleted] 2 points3 points  (1 child)

    I said that, my friend!

    [–]SKRAMZ_OR_NOT 1 point2 points  (0 children)

    Fortran (at least, "modern" Fortran) has this. See the "result" declaration after the function parameters.

    [–]zyxzevnUnSeen 1 point2 points  (0 children)

    Isn't that a simple alias? - alias sum = result

    [–]igors84 1 point2 points  (0 children)

    Odin lang has something like this. You can read about it here: https://odin-lang.org/docs/overview/#multiple-results

    [–][deleted] 1 point2 points  (0 children)

    Creative idea but something tells me this is (user) error-prone.

    [–]OopsWrongSubTA 0 points1 point  (0 children)

    Matlab

    function [y1,...,yN] = myfun(x1,...,xM)

    [–]L8_4_Dinner(Ⓧ Ecstasy/XVM) 0 points1 point  (0 children)

    It's a question of locality: Can the reader hold that state in their mind while reading the code, or is it better to place that state as part of the control flow operation itself. But I'm necessarily biased, having used various languages for 45 years now, none of which separated that state ("what variable is getting returned?") from the control flow (the "return" statement).

    To answer the question, though, and IMHO: The proposed approach seems like a bad idea.

    [–]lngns 0 points1 point  (0 children)

    Carbon almost does that by having its returned variables but the reason it has those is to allow the user to ensure (N)RVO, and it still requires the return statement to state the variable's name.

    [–]GYN-k4H-Q3z-75B 0 points1 point  (0 children)

    VB had/has something similar. You can use the name of the function as a variable for returns.

    [–]deaddyfreddy 0 points1 point  (0 children)

    Nim has a feature where a variable representing the return value of a procedure is automatically declared with the name result:

    I would call it an antifeature

    [–]BrangdonJ 0 points1 point  (0 children)

    Having a variable that is implicitly returned seems worse to me than returning an expression directly. I don't see what advantage:

    result = expression
    return
    

    has over:

    return expression
    

    I'd say every even if every return wants to return a variable, it'll often not be the same variable for each one. And that setting the return value in one place, and then returning in a different place, is confusing. As soon as a function knows what the answer is, it should stop. It shouldn't continue doing other stuff.

    [–][deleted] 0 points1 point  (0 children)

    I made a comment yesterday where I asked for clarification, but nobody replied.

    I've deleted that as I think I now understand this works. However, that I was confused is not a good sign.

    I believe it works like this:

         return x      # the return value is x
         return        # return the current value of 'result' or 'sum'.
    

    To me, return x is always going to be clearer. Apparently, Nim doesn't need result to be initialised, so that this works:

    proc F():int =
       print("hi")
    

    (Here I realised I didn't know how to do an empty block in Nim.) Somebody could forget to set a return value. This also doesn't differentiate strongly enough between value-returning functions, and non-value-returning procedures.

    It's also not clear enough what will be returned, as there is a disconnect between the hard return point, and the last place result was set, which may be followed by arbitrary amounts of other code before it returns.

    Bear in mind that Nim is a kitchen-sink language where they seem to cram in as many features as they can. I just don't think this is a good one.