I'm converting some older nim code to run under 1.2, and it uses list comprehensions pretty heavily
Mostly it's trivial to rewrite lc in terms of collect but this one is giving me trouble. Here's a stripped-down version:
import sugar
#import lc
#let ans = lc[(x,y) | (y <- 0..10,y mod 5 == 2, x <- 0..y),(int,int)]
let ans = collect(newSeq):
for y in 0..10:
if y mod 5 == 2:
for x in 0..y:
(x,y)
#error | expression '(x,y)' is of type tuple of (int,int) and must be discarded
#sugar.nim 258 col 29 | error | no tuple type for constructor
Interleaving an if statement in between the two for loops, combined with returning a tuple, is what seems to set it off.
You could work around it by defining an iterator and calling toSeq on it. Although toSeq is also buggy ... you might just want to do
var ans: seq[int]
for y in 0..10:
if y mod 5 == 2:
for x in 0..y:
ans &= x
For the functionally inclined, the zero_functional version:
import zero_functional
let ans = (0..10) --> filter(it mod 5 == 2).map((0..it)).flatten()
So after delving into this lc heavy codebase I found this pattern is particularly useful:
#foo(bar,lc[x,x <- bar, T]).baz
foo(bar,(block:collect(newSeq):
for x in bar:
x
)).baz
The block:collect idiom worked for me to truly turn collect into a composable expression, even within nested lc if blocks
The other takeaway from working with a foreign codebase was that collect is so much more clear about what it's doing, and leads to much more maintainable (or, if necessary, optimizeable) code