Re: Why does the this code work?
I have tried to declare a `varargs[string | seq[string]]` but the compiler rejects it. It seems that for `varargs`, more strict rules apply to avoid some nasty problems. But there may exist other problematic cases as Araq said. Adding – in a future version – an explicit unpacking as he suggests would simplify things (and Python people would say that explicit is better than implicit). Personally, I would not care as I have never used this feature, but `unpackVarargs` is really an ugly name :-).
Re: Why does the this code work?
I'm sure there a couple of open bugs thanks to these rules and in retrospect `f(unpackVarargs(s))` might have been the better design. Maybe for Nim version 2. :-)
Re: Why does the this code work?
You're right, thanks; There probably could be issues in convoluted cases (will try to construct one later for fun, e.g. `varargs[string | seq[string]]` , or with default conversions -- but you're right that this is unlikely to ever pop-up om regular use even if these cases can artificially be constructed.
Re: Why does the this code work?
> Varargs has to be the last parameter, openarray(s) can be anywhere. Actually they also can be anywhere. Caveat: `varargs[untyped]` in macros (specifically not `varargs[typed]` and not in template) have a bug and are currently only usable as the last parameter.
Re: Why does the this code work?
As regards implicit dereferencing, the compiler, when looking for the field "a" has to detect that "p" is a reference or a pointer, check if the referenced type is an object or a tuple and that it contains a field "a" and if this is OK generate the code for dereferencing. I don’t see that as stylistic. Other languages will detect an error as "p" has not the right type. As regards the varargs, that Python has more information at runtime than Nim, I agree. But, the expansion has to be done at compile time, not at runtime and Python lacks information about the expected argument type. When you write `def f(x):`, you don’t give the compiler a clue about the type of the elements of "x" which may be a list, an int, an dict and so on. So, if I call it with a list (without the "*"), the compiler cannot do the expansion as it doesn’t know what is expected (and to complicate things, contrary to Nim, the varargs need not to be of the same type). And your example with `printStuff(@[lines]` is wrong. If you want to pass a list of strings as a whole to a varargs, you have to declare the procedure to accept a sequence of strings, not a string. "printStuff" has been declared to accept a `varargs[string]` not a `varargs[seq[string]]`, so, when writing `printStuff(lines)` there is no way you can get a unique argument "lines" as it has not the expected type "string". But this works: proc printStuff(x: varargs[seq[string]]) = for element in x: echo element let lines = @["abc", "def"] printStuff(lines) Run Comparing Nim and Python is frequently a bad idea as the languages are fundamentally different. Python has done things a way which is logical for a dynamically typed language. And Nim does the things another way which is consistent with its nature.
Re: Why does the this code work?
> There is another case where the Nim compiler uses its knowledge of types to > help the programmer: this is the automatic dereferencing of references and > pointers. No, it's just a stylistic decision, one that some people find surprising as it is a special case. Python could have just as well automatically expanded a list into varargs - in fact, it has more information at the time of the call than Nim does, being dynamic. If you want to pass just one list argument to a vararg Nim proc, you need to call e.g. `printStuff(@[lines])`, because `printStuff(lines)` would ""unwrap"" it and provide a list of unknown length; whereas either `printStuff(lines,lines)` or `printStuff(@[lines,lines])` would result in an argument list of 2 arguments, regardless of the length of lines. This (apparent) inconsistency is the source of the confusion. Personally, I prefer the Python behaviour, but since we're past v1.0, I don't think it's worth discussing a breaking change even if everyone agrees with me (And not everyone does).
Re: Why does the this code work?
There is no reason to use a special syntax to tell the compiler that expansion is needed. Thanks to static typing, the compiler is able to see if the array or sequence must be transmitted as a whole to the procedure or must be expanded. In Python, you need a special syntax. Given a function _def f(*x)_ and a list _lst_ , if you write _f(lst)_ , _x_ will be a tuple whose first (and only) elements will be _lst_. You have to use the special syntax _f(*x)_ to transmit each element of _lst_ as an individual argument and, in this case, _x_ will contain the elements of _lst_ and not the whole list. There is another case where the Nim compiler uses its knowledge of types to help the programmer: this is the automatic dereferencing of references and pointers. Given a _ref object_ , when accessing a field _a_ via a reference _p_ , you write _p.a_ and the compiler knows that it must dereference _p_. In C, you would have to write something like _(*p).a_ or _p- >a_.
Re: Why does the this code work?
The surprise (as I understand it, and I remember it surprised me too reading the docs) is the automatic expansion of the arguments. The consistent way would be for `printStuff(lines)` above to provide 1 argument of type seq[string], in the same way each arg of `printStuff(lines, lines)` would do. As it is, a single "lines" argument results in an arbitrary length x, but two arguments result in x.len==2. e.g. in Python, you have the same functionality, but you have to prefix the arg with a `*` to say "I wish to expand this list into an argument list and not pass it as a single argument", which is consistent with the use of `*` in similar contexts, and with the passing of any other individual argument.
Re: Why does the this code work?
Besides the ability to apply function (like stringify `$`) to the parameters, varargs is nothing but an openArray with an alternative way of specifying arguments available. The normal way is still useful for e.g. when you forward parameters or when you don't know how many parameters you have and you pas over a seq: import strutils proc printStuff(x: varargs[string]) = for element in x: echo element let lines = readFile("file.txt").splitLines() # the same proc used in two occaisons # afaik in C++ you would have to write a wrapper for printStuff printStuff "Hi, how are you going?", "Here's the content of a file" printStuff lines Run
Re: Why does the this code work?
Varargs has to be the last parameter, openarray(s) can be anywhere.
Re: Why does the this code work?
Well, we may ask: Why do we have openArray proc parameter type at all, when varargs works for multiple arguments, for a seq and for an array? So varargs should be able to fully substitute openArray parameter type. Maybe varargs has more overhead, or maybe there exists cases where we want to allow passing an array or a seq, but not multiple individual parameters?
Re: Why does the this code work?
But the code is for accepting multiple individual integers, not for accepting an array/sequence. It feels like the code isn't doing 'exactly' what it was intended to do. It's sort of doing more.
Re: Why does the this code work?
Why shouldn't it 樂? The implicit conversion to a varargs/openarray (same thing) is cheap.
Why does the this code work?
proc dispayVarargs(va: varargs[int]) = echo va.len var se: seq[int] = @[2, 3, 4] dispayVarargs(se) Run I am happy that it works but it feels like breaking design.