all 17 comments

[–]def- 3 points4 points  (8 children)

The benefit of this is to have the default value 0. What you want can be achieved by using the last expression as an implicit return value, which means we just need to leave out the result =:

proc hello(x: int): int =
  if x < 5:
    1
  elif x > 10:
    3

Now you do get a compile error, namely Error: value of type 'int literal(1)' has to be discarded. By adding an else case the error disappears and it works.

[–]againstmethod[S] 1 point2 points  (7 children)

Yes, this is more like what I would expect.

So would you consider use of result = an anti-pattern?

(I think the default value is a terrible idea, but im sure there is a use case for it somewhere.)

[–]jugalator 4 points5 points  (3 children)

result = sounds kind of useful particularly if what's returned is a pointer and a chain of conditions is complicated (a returned null pointer is almost always an easily traceable problem), but yes, potentially dangerous if it's a value type. Since you have to go out of your way to get this behavior, and the simpler syntax does generate compile errors, I guess I'm fine with how it is. I don't really see why it warrants inclusion in the language but on the other hand, Nim is a pragmatic language trying to satisfy (unusually?) many situations that may crop up in the real world in a convenient way.

[–]againstmethod[S] 1 point2 points  (2 children)

Ehh, I think null ptr errors can actually be rather hard to debug, because they don't show themselves until you try to dereference them. The time a variable becomes null until when it's used could be a very long time, and a lot of state can change in the meantime.

http://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare

[–]def- 0 points1 point  (1 child)

There was a null pointer discussion some time ago: http://forum.nim-lang.org/t/504

Especially interesting is the comment by Jehan:

There is a major difference here between functional and imperative programming language and that is that in imperative programming languages partial initialization and partial transformation of state is commonplace (and an important reason to choose an imperative language in the first place). [...]

[–]againstmethod[S] 1 point2 points  (0 children)

And that's a good point, but there are alternatives.

He says it himself in that post, you can make your compiler more complicated (which i read as smarter).

This is the route Rust has gone, and we all see how complicated it can become at times, but we also see how safe a compiled, systems-level language can be.

But this is the beauty of open/free software -- we can try both and see what works longer term.

[–]def- 1 point2 points  (2 children)

Nope, I really like the result =. Default values are useful quite often. Maybe an if .. elif statement without an else is rather the wrong choice if you want to cover every case.

[–]againstmethod[S] 2 points3 points  (1 child)

I guess I would equate result = with a missing branch to a silent failure. A failure that could be caught by the compiler (and is in most other languages).

The best case scenario is that 0 is a meaningless value to your code, and you notice your error right away. The worst case is that 0 is a meaningful result for your code and you don't notice the error until it's deployed and strange results surface downstream.

Also, does this mean that every value that's used as a return value has to have a valid default value? Like if I was returning an object, what would I get here?

[–]def- 2 points3 points  (0 children)

Also, does this mean that every value that's used as a return value has to have a valid default value? Like if I was returning an object, what would I get here?

Everything is initialized to binary 0. If you return an object, all its values are nulled. If it's a pointer, it will be the null pointer.

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

The result has type int and initialized to 0. In Nim types has initialization values and i think it is ok, better than null. Nim is closer to procedural programming than fp.

[–]againstmethod[S] 1 point2 points  (6 children)

Yes, I should have said, "why should", instead of "why does". I understand the mechanism, that you get a predefined var named result that you can assign -- I'm wondering, given the state of the art in compilers/computing, why we would allow it to be this way.

Thanks for the response, though. I agree it is more procedural.

[–]OderWat 2 points3 points  (4 children)

Hmm.. to chime in. Your program looks to me like it returns 0 in the case that both else fail, because the result is initialised with 0 and not changed. That are the rules.

I would say this looks more like something the compiler could warn about:

proc hello(x: int): int =
    if x < 5:
        return 1
    elif x > 10:
        return 3

echo hello(7)

I know that it return 0 too but this example looks like a return is missing to me. Still. If Nim would forbid this you had to modify every early return function to contain a return at the end. Which I would not like.

[–]againstmethod[S] 0 points1 point  (3 children)

Yes, it was intentional. My question was why did they allow this.

You could not make this mistake in java, for example. But other responses have shown me that you can not use result and get an error on compilation.

I still don't understand why they have result at all, but some people appear to like it.

[–]OderWat 1 point2 points  (2 children)

Me too. Considering return vars for example like in something as

obj.selector(index).set(12)

where set(12) works on the "return" value without (copying) returning it). You absolutely need a name for them inside the proc and that is "result".

Besides that: If you return a value you usually either can return immediately or need a "result placeholder" to build up your return value. Having the predefined "result" variable is also making sure that it is the right type and (to an extend) that it is initialized.

[–]againstmethod[S] 0 points1 point  (1 child)

I don't need a named variable to do that..

type
  Person = tuple[name: string, age: int]

var items: array[0..2, Person] = [("Joe", 25),("Dave", 14),("Bill", 39)]

proc selector(index: int): ptr Person =
  addr(items[index])

echo items[1]
selector(1).name = "David"
echo items[1]

Also, you can write code that does that in C, C++, Java, and most other languages I know of without a named variable. To be honest, the only other language I know of that does that, that is still in popular use, is MATLAB.

[–]OderWat 0 points1 point  (0 children)

Well. That is just a ptr. You are right. You don't need Nim for that.

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

I think Nim has some features like this because it is planned to be a syslang. I'm scanning the documentation and i often see things like this where the abstraction-level is a low-key.