Skip to content

Combining with empty list should produce a list #6026

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
VasLem opened this issue May 2, 2025 · 2 comments
Open

Combining with empty list should produce a list #6026

VasLem opened this issue May 2, 2025 · 2 comments

Comments

@VasLem
Copy link

VasLem commented May 2, 2025

Bug report

A combine operation should always produce a list, but the output is instead depending on whether the input arguments list size is 0:

Example:


workflow {
    names = channel.empty().toList()    
    names.combine(channel.value("empty combined with value")).view{it}
    names = channel.value("x").toList()    
    names.combine(channel.value("x combined with value")).view{it}

}

Output:

[x, x combined with value]
empty combined with value

This creates a lot of problems in downstream operations that expect a certain number of input arguments.
It would be far more preferable to have a deterministic number of output from the combine operation, independent on the content of each variable. In the above operation, I would expect [[], "x combined with value], as this is the expected result of a cartesian product

Environment

  • Nextflow version: 25.03.1-edge
@VasLem VasLem changed the title Combining with empty list does not produce expected result Combining with empty list should produce a list May 2, 2025
@VasLem VasLem changed the title Combining with empty list should produce a list Combining with empty list should produce nothing May 2, 2025
@VasLem VasLem changed the title Combining with empty list should produce nothing Combining with empty list should produce a list May 2, 2025
@bentsherman
Copy link
Member

It is because of how combine constructs the result:

@PackageScope
@CompileDynamic
def tuple( List p, a, b ) {
List result = new LinkedList()
result.addAll(p)
addToList(result, a)
addToList(result, b)
result.size()==1 ? result[0] : result
}

It creates a tuple, appends the elements of the left and right tuples, and then emits either the tuple or the first element if the tuple has only one element

It should be possible to work around this by wrapping the empty list in another list:

names = channel.empty().toList().map { [it] }
names.combine(channel.value("empty combined with value")).view()

But I am hesitant to change the behavior of combine because it could affect other use cases. Unfortunately operators like combine have some unclear semantics that makes it hard for us to reason about what is and isn't "correct". Instead I will try to address this as part of operators v2.

@VasLem
Copy link
Author

VasLem commented May 2, 2025

Hi Ben, thanks for the detailed explanation. Why would the following affect other use cases?

@PackageScope 
 @CompileDynamic 
 def tuple( List p, a, b ) { 
     List result = new LinkedList() 
     result.addAll(p) 
     result.add(a) 
     result.add(b)  
    result
 } 

I mean, this is supposed to be the cartesian product, concatenating the elements from the combination, without any flattening, and returning it as is, no? I also have followed the discussion in the past issues and in all of them the way the lists are handled is completely not how it is explained in the documentation, and very counter-intuitive. I have a lot of combines in my workflows, and any empty list case would have to be dealt with what you propose. For now, what I am doing is to use ifEmpty to all the possible places where a channel is consumed before combination, and giving it a list of dummy variables, which are checked downstream, with filter operations....

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants