Open liquidev opened 3 years ago
While the proposed syntax is really nice, I'm not entirely sure that it is justified to add another way of calling functions.
Example with getHeader
actually is ambiguous - how do I know if it is a parameter passing and assignment to some external variable defined elsewhere?
do
statement multiple times to pass large blocks expressions to procedure parametersimport macros, strutils
macro kvCall*(head, nodes: untyped): untyped =
result = newCall(head)
for node in nodes:
result.add nnkExprEqExpr.newTree(node[0], node[1][0])
proc manyLongParams(a, b, c: string) =
echo "a: ", a
echo "b: ", b
echo "c: ", c
manyLongParams do:
"first parameter"
do:
"second parameter"
do:
"Third parameter"
kvCall manyLongParams:
a: "first argument"
c: "C parameter"
b: "B parameter"
The getHeader
example actually is just as ambiguous as any of my examples presented above. My idea was that the parameter block would introduce a new scope with all the parameters available as pseudo-variables, so assignment to parameters would shadow assignment to variables from outer scopes, however, these pseudo-variables would not be referencable, eg. in the lineRectangle
examples position = size
would not work (unless size
is a variable declared somewhere outside of the block).
If it could be made into a macro, I think it should land in the stdlib in the sugar
module. Otherwise we'll end up with possibly tens of different macros, people reading code will have to refer to each one's documentation. I'd rather have this be standardized.
I think the current solution of command syntax is sufficient for these examples, but if you don't want to "pollute scope", use a template instead of a variable.
lineRectangle graphics,
position = tilemap.tileSize * (position.vec2f + offset.vec2f),
size = tilemap.tileSize,
thickness = 8,
color = colGreen
Note that you can name unnamed parameters (and reorder them).
EDIT: added the example for getHeader
:
"lzma.h".getHeader
giturl = "https://github.com/xz-mirror/xz",
dlurl = "https://tukaani.org/xz/xz-$1.tar.gz",
conanuri = "xz_utils",
jbburi = "xz",
outdir = baseDir,
conFlags = "--disable-xz --disable-xzdec --disable-lzmadec --disable-lzmainfo"
If only your getHeader
example parsed I wouldn't be opening this RFC :) Allowing for a more elegant syntax like this is the point here. lineRectangle graphics
is kind of backwards from what I'd want, but I guess that can also work.
It's a matter of style, but I prefer to emphasize the most significant part of the statement first; I'm assuming that this is lineRectangle
or "lzma.h"
. You're right, I do kinda wish the getHeader
worked. Again, I think a template is a reasonable concession to not altering the syntax.
Yet another call syntax is not negotiable, sorry. Also, you can get what you want via some multiLineCall
macro:
let set = multiLineCall(TileSideSet):
connectsTo(dx = 1, dy = 0).ord * tsseRight or
connectsTo(dx = 0, dy = 1).ord * tsseBottom or
connectsTo(dx = -1, dy = 0).ord * tsseLeft or
connectsTo(dx = 0, dy = -1).ord * tsseTop
If it could be made into a macro, I think it should land in the stdlib in the sugar module. Otherwise we'll end up with possibly tens of different macros, people reading code will have to refer to each one's documentation. I'd rather have this be standardized.
There is some overlap with our existing with
macro. Which was developed after people used comparable means for some time. Starting with your ideas as a Nimble package is a good development process.
Starting with your ideas as a Nimble package is a good development process.
Not trying to plug but https://github.com/metagn/spread seems to do what is described here
Abstract
It is currently possible to pass a block to the last parameter of a procedure. This is a proposal to allow for passing any amount of (potentially named) parameters.
Motivation
Currently, passing lots of parameters to a procedure can get quite messy. This is very apparent when building trees with macros, and the NEP-1 code style guide does not make this much cleaner either, as procedure parameters should be aligned to the call's opening parenthesis
(
which creates an awkward indent that's hard to deal with in editors, and pushes parameters very far to the right, which makes things like building trees with macros quite messy.Description
My idea was to extend the currently available call-with-block syntax to interpret subsequent (non-void) statements as parameters. This would also allow for passing lots of named parameters to procedures in a very elegant way, possibly also help with RFC #264. This would only work with procedures, as getting this to work with templates and macros, which can accept arbitrary code blocks, could be quite difficult, and possibly a breaking change, if all ambiguities need to be taken into account. If anything, only macros and templates that don't accept
untyped
would be eligible.Given this procedure:
The current way of passing parameters can get messy, especially if a given parameter is some complex expression:
If an expression grows to the 80-column limit, it can be quite difficult to break it down. The best solution in that case would be polluting the scope with a temporary variable that's only used once.
A different solution would be this:
But frankly, the extra
)
at the end makes the call look ugly, and this is especially apparent when constructing trees using macros:NEP-1 doesn't help this either, usually pushing parameters so far to the right that an extra temporary variable is needed to fit in 80 columns:
The current call-with-block syntax allows for this:
Which is quite useful when converting a complex expression to a distinct type:
However, I think such syntax would be equally useful for calling procedures with multiple parameters. Consider the previous
lineRectangle
example:The big upside is that now the same call looks far more natural and doesn't require a huge indent that's hard to handle properly by editors.
The complex expression situation is also much improved:
As can be seen in the example above, the
position
parameter is much easier to fit in the 80 column limit, or at least can be formatted nicely. Another advantage is that users of editors with poor auto-indentation don't have to hassle with deindenting long auto-indented lines manually.This syntax could also be extended to support object constructors, which is probably a far more common use case where having lots of fields is quite common:
The primary downside of such syntax is that it's yet another thing to learn about, and yet another thing to maintain in the compiler. If it cannot be implemented for templates and macros, it would also lead to an inconsistency where it can only be used with procedure calls.
Examples
Going back to the macro example, let's examine how it would improve if my RFC is implemented.
Before
After
There's far less noise introduced by parentheses and commas, and it's far easier to add new parameters to any of the calls, as you don't have to remember to add a comma after each one.
Let's take a look at another example taken from nimterop.
Before
After
Since
getHeader
is a macro, this would need the hypothetical implementation for templates and macros, but if that's ever implemented, here's how the above example would improve:Without my syntax, the extra
)
at the end introduces a weird almost-empty line after the call. This is fixed, and even improved upon by the extended call-with-block syntax, allowing to move the header in questionlzma.h
to a regular()
argument list.Backward incompatibility
Since the call-with-block syntax is mostly used with untyped templates and macros, and this RFC does not target them, there shouldn't be any backward incompatibilities introduced. The original behavior of interpreting a block as the last parameter will stay the same, as this RFC only makes the call-with-block syntax support passing more than one parameter.