mratsim / loop-fusion

Loop efficiently over a variadic number of containers
Other
40 stars 3 forks source link

Feature: Filter - allow returning no value during iteration. #6

Open mratsim opened 6 years ago

mratsim commented 6 years ago

As explained in the readme:

Expressions must return value of the same types, i.e. you can't return void/no value at some iterations and a concrete value at other iterations.

This prevents:

import ../loopfusion

let a = @[1, 2, 3]
let b = @[4, 5, 6]

let c = forZip(x in a, y in b):
  if x mod 2 != 0:
    y

doAssert c == @[1, 3] # Error: expression 'y' is of type 'int' and has to be discarded

Nim list comprehension can serve as inspiration:

https://github.com/nim-lang/Nim/blob/devel/lib/pure/sugar.nim

type ListComprehension = object
var lc*: ListComprehension

macro `[]`*(lc: ListComprehension, comp, typ: untyped): untyped =
  ## List comprehension, returns a sequence. `comp` is the actual list
  ## comprehension, for example ``x | (x <- 1..10, x mod 2 == 0)``. `typ` is
  ## the type that will be stored inside the result seq.
  ##
  ## .. code-block:: nim
  ##
  ##   echo lc[x | (x <- 1..10, x mod 2 == 0), int]
  ##
  ##   const n = 20
  ##   echo lc[(x,y,z) | (x <- 1..n, y <- x..n, z <- y..n, x*x + y*y == z*z),
  ##           tuple[a,b,c: int]]

  expectLen(comp, 3)
  expectKind(comp, nnkInfix)
  expectKind(comp[0], nnkIdent)
  assert($comp[0].ident == "|")

  result = newCall(
    newDotExpr(
      newIdentNode("result"),
      newIdentNode("add")),
    comp[1])

  for i in countdown(comp[2].len-1, 0):
    let x = comp[2][i]
    expectMinLen(x, 1)
    if x[0].kind == nnkIdent and $x[0].ident == "<-":
      expectLen(x, 3)
      result = newNimNode(nnkForStmt).add(x[1], x[2], result)
    else:
      result = newIfStmt((x, result))

  result = newNimNode(nnkCall).add(
    newNimNode(nnkPar).add(
      newNimNode(nnkLambda).add(
        newEmptyNode(),
        newEmptyNode(),
        newEmptyNode(),
        newNimNode(nnkFormalParams).add(
          newNimNode(nnkBracketExpr).add(
            newIdentNode("seq"),
            typ)),
        newEmptyNode(),
        newEmptyNode(),
        newStmtList(
          newAssignment(
            newIdentNode("result"),
            newNimNode(nnkPrefix).add(
              newIdentNode("@"),
              newNimNode(nnkBracket))),
          result))))
mratsim commented 5 years ago

Devel now has for expressions which are probably better than the hack that tests if the type is void.

See test: https://github.com/nim-lang/Nim/blob/devel/tests/macros/tcollect.nim