I'm not sure if that's a correct subject for this question. Originally the question started from here https://github.com/Araq/Nimrod/issues/1742. The main intention is to insert a defer stmt from a template into enclosing scope.
var currentContext: Context
template setContextInScope(ctx: Context) =
let oldContext = currentContext
currentContext = ctx
defer: currentContext = oldContext
Currently defer statement is evaluated as if the template defines it's own scope, and that is correct according to the docs. Maybe templates should be changed so that they don't start a new scope, and if anyone needs it, he should use a block stmt? What do you think?I think this would be dangerous. The same problem with C Macros. If you forget to place the block or braces, then the user may get unexpected results.
Example:
template add_numbers(a, b: int): expr = a + b
proc main() =
let
a = 2
b = 3
c = 4
echo "" & $(a * add_numbers(b, c)) # result is 14 because the expr of the template is evaluated first
main()
yglukhov said: Maybe templates should be changed so that they don't start a new scope.
I understand , that a * (b + c) will become a * b + c. Maybe the immediate, dirty and inject pragmas can help to accomplish what you like to do.
I understand , that a * (b + c) will become a * b + c
That's not what I meant. I don't mind that expr templates still produce a solid (expression), and that's a good thing, I guess.
Maybe the immediate, dirty and inject pragmas can help to accomplish what you like to do.
defer behaviour is not affected by immediate and dirty, and it sounds like it should not. Introducing a new sameScope or dontScope or whatever pragma might be a reasonable solution.
yglukhov: Introducing a new sameScope or dontScope or whatever pragma might be a reasonable solution.
If there is no other way, this sounds good to me. Seems to fit the language design.
Would it make sense, also to be able to specify per defer if it is going to the current or the calling scope? Currently I have no idea for an example where I would need to do this.
Um.. language-wise, templates don't have a scope (at least, they aren't supposed to). This is evidenced by the code below:
template breakOff =
break # If the template had a scope, then the break would be invalid.
for i in countUp(0, 10):
echo i
if i == 5:
breakOff
Templates only appear to have scopes because their variables are hygienic by default.
The bug you encountered is specific to the 'defer' mechanism. If I had to guess, it's because the compiler is scanning the body of the template when generating the internal try..finally, not because templates have their own scopes.
Well there is a distinction between 'scope' and 'block', so let's not confuse these terms. Indeed a template has no 'scope' and hygiene works differently. However a template gets a new block and so templates that hijack the outer block via 'defer' are not possible. It works as I intended it to work since I don't like hijacking of blocks via templates. 'defer' is a keyword for this reason and not some builtin template.
That said, your 'break' example is convincing; the current design is not really consistent.
so let's not confuse these terms
Maybe it's worth fixing the docs, as they're not very clear regarding this point.
From manual: A template is a hygienic macro and so opens a new scope.
It works as I intended it to work since I don't like hijacking of blocks via templates.
Well, maybe it's not that bad after all?
P.S. This forum syntax cheatsheet leads to the wrong page, so sorry for my formatting =).