Before we begin: I'm new to Nim and it's been a while since I did anything to do with AST, parsing, etc. so my suggestions may be whacky. If they are, simply say so and turn the discussion in the right direction.
Where we're at
List comprehension is currently available through the future module. For loops as expressions have been suggested to replace it but what should their capabilities be, and what should the syntax look like?
Requirements
(The pros and cons of each are probably best found by providing examples of each.)
(I'm guessing B, since expressiveness is a higher priority than elegance.)
Should it be implemented on parser level, as a macro, or something else? I'm not very familiar with the any of these things, but it I'm guessing they put different limitations on the syntax and provide varying levels of efficiency.
For Y, this is what I had in mind:
# @[2, 3, 4]
var a = for x in [1, 2, 3]: x+1
# {2, 3, 4}
var a: set[int] = for x in [1, 2, 3]: x+1
# With input "A", "B", "C": {"A", "B", "C"} and @["A", "B", "C"]
# With input "A", "A", "C": {"A", "C"} and @["A", "A", "C"]
var a: set[string] = for x in 1..3: readLine(stdin)
var a = for x in 1..3: readLine(stdin)
And lastly, here's a wall of code that contains my not-so-well-structured notes:
import strutils, algorithm, math
var bowlSizes = (var s: seq[int] = @[]; for _ in 1..3: s.add(parseInt(readLine(stdin))); s)
# var bowlSizes = lc[parseInt(readLine(stdin)) | (_ <- 1..3), int]
bowlSizes.sort(system.cmp[int])
echo bowlSizes[1]
"""Syntax suggestions; line one describes the syntax, line two is how it could be expanded."""
# Syntax 1
var a: seq[T] = for x in y: expr
var a: seq[T] = (var s: seq[T] = seq[T](); for x in y: (var e: T = expr; if e != nil: s.add(e)); s)
# Syntax 2, rule 1
var a: seq[T] = expr for x in y
var a: seq[T] = (var s: seq[T] = seq[T](); for x in y: (var e: T = expr; if e != nil: s.add(e)); s)
# Syntax 2, rule 2
var a: seq[T] = expr for x in y cond
var a: seq[T] = (var s: seq[T] = seq[T](); for x in y: (var e: T = expr; cond: (if e != nil: s.add(e))); s)
"""Random examples"""
# Syntax 1 examples
A) var a: seq[int] = for x in [1, 2, 3, 4]: if x mod 2 == 0: x else: x + 1 # @[2, 2, 4, 4]
B) var a: seq[int] = for x in [1, 2, 3, 4]: if x mod 2 == 0: x # @[2, 4]
C) var a = ... # type inference
D) var a = for x in 0..10: if x mod 2 == 0: (if x < 5: 5 - x else: x - 5)
E1) var a = for x in [1, 2, 3, 4, 5]: for y in [1, 1, 2, 3, 5]: if x mod y == 0: x / y
E2)
var a =
for x in [1, 2, 3, 4, 5]:
for y in [1, 1, 2, 3, 5]:
if x mod y == 0:
x / y
# Syntax 2 examples
A) var a: seq[int] = x if x mod 2 == 0 else x + 1 for x in [1, 2, 3, 4] # @[2, 2, 4, 4]
B1) var a: seq[int] = x for x in [1, 2, 3, 4] if x mod 2 == 0 # @[2, 4]
B2) var a: seq[int] = x if x mod 2 == 0 for x in [1, 2, 3, 4] # @[2, 4]
C) var a = ... # type inference
D) var a: seq[int] = 5 - x if x < 5 else x - 5 for x in 0..10 if x mod 2 == 0
E1) var a = x / y for x in [1, 2, 3, 4, 5] for y in [1, 1, 2, 3, 5] if x mod y == 0
E2)
var a = x / y
for x in [1, 2, 3, 4, 5]
for y in [1, 1, 2, 3, 5]
if x mod y == 0
"""More examples. From http://www.secnetix.de/olli/Python/list_comprehensions.hawk"""
# Plain math
S = {x^2 : x in {0 ... 9}}
V = (1, 2, 4, 8, ..., 2^12)
M = {x | x in S and x even}
# Python
S = [x**2 for x in range(10)]
V = [2**i for i in range(13)]
M = [x for x in S if x % 2 == 0]
# Syntax 1
var S = for x in 0..9: x^2
var V = for i in 0..12: 2^i
var M = for x in S: if x mod 2 == 0: x
# Syntax 2
var S = x^2 for x in 0..9
var V = 2^i for i in 0..12
var M = x for x in S if x mod 2 == 0
OR var M = x if x mod 2 == 0 for x in S
"""Allow any collection that implements add(). Is it unintuitive or harder to implement?"""
# Variation of Syntax 1
var a: Y[T] = for x in y: expr
var a: Y[T] = (var s: Y[T] = Y[T](); for x in y: (var e: T = expr; if e != nil: s.add(e)); s)
# Assume seq if not specified?
var a = for x in y: expr
var a: seq[T] = ...
# Variation of Syntax 2
var a: Y[T] = expr for x in y
var a: Y[T] = expr for x in y cond
var a: Y[T] = (var s: Y[T] = Y[T](); ...
I'm surprised no one replied to your thread yet.
The goal is to have "for" be a expression so it could sit on the right-hand side of an assignment.
I'm not sure how it will be in practice for the: initialization - for loop - return
I see a post for the first time, so it is likely has been blocked for sometime .
Couple of thoughts in loud:
In Nim expressions have already a proper type, no need wrap them into let|var assignments. for x in [1, 2, 3, 4]: if x mod 2 == 0: x else: x + 1 should be a already valid.
I think it makes sense for expression to return an iterator that can be folded into any container type afterwards: seq, array or custom.
It is also probably the easiest from the implementation perspective, you just sprinkle yield in the right places.