# std/sugar

Dark Mode

- Manual
- Standard library
- Index
- Compiler docs
- Fusion docs
- devel, stable

Search:
Source Edit

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

# Imports

since, macros, underscored_calls

# Macros

    macro `->`(p, b: untyped): untyped

  Syntax sugar for procedure types. It also supports pragmas.
  Warning: Semicolons can not be used to separate procedure arguments.

  Example:

      proc passTwoAndTwo(f: (int, int) -> int): int = f(2, 2)
      # is the same as:
      # proc passTwoAndTwo(f: proc (x, y: int): int): int = f(2, 2)

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

      proc passOne(f: (int {.noSideEffect.} -> int)): int = f(1)
      # is the same as:
      # proc passOne(f: proc (x: int): int {.noSideEffect.}): int = f(1)

      assert passOne(x {.noSideEffect.} => x + 1) == 2

  Source Edit

    macro `=>`(p, b: untyped): untyped

  Syntax sugar for anonymous procedures. It also supports pragmas.
  Warning: Semicolons can not be used to separate procedure arguments.

  Example:

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

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

      type
        Bot = object
          call: (string {.noSideEffect.} -> string)

      var myBot = Bot()

      myBot.call = (name: string) {.noSideEffect.} => "Hello " & name & ", I'm a bot."
      assert myBot.call("John") == "Hello John, I'm a bot."

      let f = () => (discard) # simplest proc that returns void
      f()

  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 std/strformat

      var myClosure: () -> string
      for i in 5..7:
        for j in 7..9:
          if i * j == 42:
            capture i, j:
              myClosure = () => fmt"{i} * {j} = 42"
      assert myClosure() == "6 * 7 = 42"

  Source Edit

    macro collect(body: untyped): untyped

  Same as `collect` but without an `init` parameter.

  Example:

      import std/[sets, tables]
      let data = @["bird", "word"]

      # seq:
      let k = collect:
        for i, d in data.pairs:
          if i mod 2 == 0: d
      assert k == @["bird"]

      ## HashSet:
      let n = collect:
        for d in data.items: {d}
      assert n == data.toHashSet

      ## Table:
      let m = collect:
        for i, d in data.pairs: {i: d}
      assert m == {0: "bird", 1: "word"}.toTable

      # avoid `collect` when `sequtils.toSeq` suffices:
      assert collect(for i in 1..3: i*i) == @[1, 4, 9] # ok in this case
      assert collect(for i in 1..3: i) == @[1, 2, 3] # overkill in this case
      from std/sequtils import toSeq
      assert toSeq(1..3) == @[1, 2, 3] # simpler

  Source Edit

    macro collect(init, body: untyped): untyped

  Comprehension for seqs/sets/tables.

  The last expression 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`.

  Example:

      import std/[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 = collect(initHashSet()):
        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

    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.

  See also: `dumpToString` which is more convenient and useful since it expands intermediate templates/macros, returns a string instead of calling `echo`, and works with statements and expressions.

  Example: cmd: -r:off

      let
        x = 10
        y = 20
      dump(x + y) # prints: `x + y = 30`

  Source Edit

    macro dumpToString(x: untyped): string

  Returns the content of a statement or expression `x` after semantic analysis, useful for debugging.

  Example:

      const a = 1
      let x = 10
      assert dumpToString(a + 2) == "a + 2: 3 = 3"
      assert dumpToString(a + x) == "a + x: 1 + x = 11"
      template square(x): untyped = x * x
      assert dumpToString(square(x)) == "square(x): x * x = 100"
      assert not compiles dumpToString(1 + nonexistent)
      import std/strutils
      assert "failedAssertImpl" in dumpToString(assert true) # example with a statement

  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 std/algorithm

      let a = @[1, 2, 3, 4, 5, 6, 7, 8, 9]
      assert a.dup(sort) == sorted(a)

      # Chaining:
      var aCopy = a
      aCopy.insert(10)
      assert a.dup(insert(10), sort) == sorted(aCopy)

      let s1 = "abc"
      let s2 = "xyz"
      assert s1 & s2 == s1.dup(&= s2)

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

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

      let c = "xyz"

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

  Source Edit