In Groovy, a local variable can have the same name as a class member, likely because class members are stored on the heap while local variables are stored on the stack.
Because process and workflow inputs in Nextflow are injected via closure delegate, they are effectively class members, which means they can be overridden by local variables in the same way:
workflow PREPARE_GENOME {
take:
fasta
main:
def fasta = '...' // redeclare as local variable
Channel.of( 1, 2, 3 )
.map { fasta -> fasta } // redeclare as closure parameter
.view()
}
This is not allowed in a function, since a function parameter is just another local variable on the stack and would be clobbered:
def func(fasta) {
def fasta = '...' // not allowed
def cl = { fasta -> // not allowed
// ...
}
}
As a result, many Nextflow pipelines have gotten into the habit of re-declaring local variables over process/workflow inputs. It doesn't cause any issues during execution, although it might be confusing to read.
However, the language server currently declares process/workflow inputs as if they were function parameters, not class members, which means that all of these re-declared local variables are errors.
This PR makes the language server more aligned with current Nextflow semantics, which will reduce the number of errors for some users.
But first, I think we should consider -- do we want these semantics? Right now, the rules around variable scopes are unclear because it's just "whatever Groovy does". If we want to be more clear/strict, now might be the best time to enforce it, while we are clarifying other aspects of the language.
Some other scattered thoughts:
the current semantics seems to be an accident of the DSL implementation rather than an intentional choice
by the way, variables declared in the process/workflow body without def are treated the same as inputs -- they become "class members" of the closure delegate, so they can also be overridden. this seems like another accident, and also is not covered by this PR
I would like to allow closures to redeclare local variables, since they should have their own scope, but I need to see if this would actually work in the JVM. Java lambdas don't allow this, even though lambdas/closures should have their own stack frame. I could probably make it work with closure delegates, but eventually it'd be nice to replace closures with lambdas under the hood for better performance
In Groovy, a local variable can have the same name as a class member, likely because class members are stored on the heap while local variables are stored on the stack.
Because process and workflow inputs in Nextflow are injected via closure delegate, they are effectively class members, which means they can be overridden by local variables in the same way:
This is not allowed in a function, since a function parameter is just another local variable on the stack and would be clobbered:
As a result, many Nextflow pipelines have gotten into the habit of re-declaring local variables over process/workflow inputs. It doesn't cause any issues during execution, although it might be confusing to read.
However, the language server currently declares process/workflow inputs as if they were function parameters, not class members, which means that all of these re-declared local variables are errors.
This PR makes the language server more aligned with current Nextflow semantics, which will reduce the number of errors for some users.
But first, I think we should consider -- do we want these semantics? Right now, the rules around variable scopes are unclear because it's just "whatever Groovy does". If we want to be more clear/strict, now might be the best time to enforce it, while we are clarifying other aspects of the language.
Some other scattered thoughts:
the current semantics seems to be an accident of the DSL implementation rather than an intentional choice
by the way, variables declared in the process/workflow body without
def
are treated the same as inputs -- they become "class members" of the closure delegate, so they can also be overridden. this seems like another accident, and also is not covered by this PRI would like to allow closures to redeclare local variables, since they should have their own scope, but I need to see if this would actually work in the JVM. Java lambdas don't allow this, even though lambdas/closures should have their own stack frame. I could probably make it work with closure delegates, but eventually it'd be nice to replace closures with lambdas under the hood for better performance