sugar

This module implements nice syntactic sugar based on Nim's macro system.

Macros

macro `=>`(p, b: untyped): untyped
Syntax sugar for anonymous procedures. It also supports pragmas.

Example:

proc passTwoAndTwo(f: (int, int) -> int): int =
  f(2, 2)

doAssert passTwoAndTwo((x, y) => x + y) == 4

type
  Bot = object
    call: proc (): string {.nosideEffect.}

var myBot = Bot()

myBot.call = () {.nosideEffect.} => "I'm a bot."
doAssert myBot.call() == "I'm a bot."
  Source Edit
macro `->`(p, b: untyped): untyped
Syntax sugar for procedure types.
proc pass2(f: (float, float) -> float): float =
  f(2, 2)

# is the same as:

proc pass2(f: proc (x, y: float): float): float =
  f(2, 2)
  Source Edit
macro dump(x: untyped): untyped

Dumps the content of an expression, useful for debugging. It accepts any expression and prints a textual representation of the tree representing the expression - as it would appear in source code - together with the value of the expression.

As an example,

let
  x = 10
  y = 20
dump(x + y)

will print x + y = 30.

  Source Edit
macro capture(locals: varargs[typed]; body: untyped): untyped
Useful when creating a closure in a loop to capture some local loop variables by their current iteration values. Example:
import strformat, sequtils, sugar
var myClosure : proc()
for i in 5..7:
  for j in 7..9:
    if i * j == 42:
      capture i, j:
        myClosure = proc () = echo fmt"{i} * {j} = 42"
myClosure() # output: 6 * 7 == 42
let m = @[proc (s: string): string = "to " & s, proc (s: string): string = "not to " & s]
var l = m.mapIt(capture(it, proc (s: string): string = it(s)))
let r = l.mapIt(it("be"))
echo r[0] & ", or " & r[1] # output: to be, or not to be
  Source Edit
macro dup[T](arg: T; calls: varargs[untyped]): T

Turns an in-place algorithm into one that works on a copy and returns this copy, without modifying its input.

This macro also allows for (otherwise in-place) function chaining.

Since: Version 1.2.

Example:

import algorithm

var a = @[1, 2, 3, 4, 5, 6, 7, 8, 9]
doAssert a.dup(sort) == sorted(a)
# Chaining:
var aCopy = a
aCopy.insert(10)

doAssert a.dup(insert(10), sort) == sorted(aCopy)

var s1 = "abc"
var s2 = "xyz"
doAssert s1 & s2 == s1.dup(&= s2)

proc makePalindrome(s: var string) =
  for i in countdown(s.len-2, 0):
    s.add(s[i])

var c = "xyz"

# An underscore (_) can be used to denote the place of the argument you're passing:
doAssert "".dup(addQuoted(_, "foo")) == "\"foo\""
# but `_` is optional here since the substitution is in 1st position:
doAssert "".dup(addQuoted("foo")) == "\"foo\""

# chaining:
# b = "xyz"
var d = dup c:
  makePalindrome # xyzyx
  sort(_, SortOrder.Descending) # zyyxx
  makePalindrome # zyyxxxyyz

doAssert d == "zyyxxxyyz"
  Source Edit
macro collect(init, body: untyped): untyped

Comprehension for seq/set/table collections. init is the init call, and so custom collections are supported.

The last statement of body has special syntax that specifies the collection's add operation. Use {e} for set's incl, {k: v} for table's []= and e for seq's add.

The init proc can be called with any number of arguments, i.e. initTable(initialSize).

Example:

import sets, tables
let data = @["bird", "word"]
## seq:
let k = collect(newSeq):
  for i, d in data.pairs:
    if i mod 2 == 0: d

assert k == @["bird"]
## seq with initialSize:
let x = collect(newSeqOfCap(4)):
  for i, d in data.pairs:
    if i mod 2 == 0: d

assert x == @["bird"]
## HashSet:
let y = initHashSet.collect:
  for d in data.items: {d}

assert y == data.toHashSet
## Table:
let z = collect(initTable(2)):
  for i, d in data.pairs: {i: d}

assert z == {0: "bird", 1: "word"}.toTable
  Source Edit