diff options
-rw-r--r-- | changelog.md | 14 | ||||
-rw-r--r-- | lib/pure/sugar.nim | 66 | ||||
-rw-r--r-- | lib/std/private/underscored_calls.nim | 38 | ||||
-rw-r--r-- | lib/std/with.nim | 58 |
4 files changed, 147 insertions, 29 deletions
diff --git a/changelog.md b/changelog.md index 7be7a722b..f106ac2c2 100644 --- a/changelog.md +++ b/changelog.md @@ -52,7 +52,7 @@ `CountTable.pop`, `Table.pop` - To `strtabs.nim`, added `StringTable.clear` overload that reuses the existing mode. - Added `browsers.osOpen` const alias for the operating system specific *"open"* command. -- Added `sugar.outplace` for turning in-place algorithms like `sort` and `shuffle` into +- Added `sugar.dup` for turning in-place algorithms like `sort` and `shuffle` into operations that work on a copy of the data and return the mutated copy. As the existing `sorted` does. - Added `sugar.collect` that does comprehension for seq/set/table collections. @@ -71,6 +71,18 @@ - Added `resetOutputFormatters` to `unittest` +- Added a `with` macro for easy function chaining that's available + everywhere, there is no need to concern your APIs with returning the first argument + to enable "chaining", instead use the dedicated macro `with` that + was designed for it. For example: + +```nim + + + +``` + + ## Library changes - `asynchttpserver` added an iterator that allows the request body to be read in diff --git a/lib/pure/sugar.nim b/lib/pure/sugar.nim index f18557e0e..047280049 100644 --- a/lib/pure/sugar.nim +++ b/lib/pure/sugar.nim @@ -191,31 +191,41 @@ macro capture*(locals: varargs[typed], body: untyped): untyped {.since: (1, 1).} result.add(newProc(newEmptyNode(), params, body, nnkProcDef)) for arg in locals: result.add(arg) -macro outplace*[T](arg: T, call: untyped; inplaceArgPosition: static[int] = 1): T {.since: (1, 1).} = - ## Turns an `in-place`:idx: algorithm into one that works on - ## a copy and returns this copy. The second parameter is the - ## index of the calling expression that is replaced by a copy - ## of this expression. - ## **Since**: Version 1.2. - runnableExamples: - import algorithm - - var a = @[1, 2, 3, 4, 5, 6, 7, 8, 9] - doAssert a.outplace(sort()) == sorted(a) - #Chaining: - var aCopy = a - aCopy.insert(10) - - doAssert a.outplace(insert(10)).outplace(sort()) == sorted(aCopy) +when (NimMajor, NimMinor) >= (1, 1): + import std / private / underscored_calls + + macro dup*[T](arg: T, calls: varargs[untyped]): T = + ## Turns an `in-place`:idx: algorithm into one that works on + ## a copy and returns this copy. + ## **Since**: Version 1.2. + runnableExamples: + 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) + + result = newNimNode(nnkStmtListExpr, arg) + let tmp = genSym(nskVar, "dupResult") + result.add newVarStmt(tmp, arg) + expectKind calls, nnkArgList + let body = + if calls.len == 1 and calls[0].kind in {nnkStmtList, nnkStmtListExpr}: + calls[0] + else: + calls + for call in body: + result.add underscoredCall(call, tmp) + result.add tmp - expectKind call, nnkCallKinds - let tmp = genSym(nskVar, "outplaceResult") - var callsons = call[0..^1] - callsons.insert(tmp, inplaceArgPosition) - result = newTree(nnkStmtListExpr, - newVarStmt(tmp, arg), - copyNimNode(call).add callsons, - tmp) proc transLastStmt(n, res, bracketExpr: NimNode): (NimNode, NimNode, NimNode) {.since: (1, 1).} = # Looks for the last statement of the last statement, etc... @@ -312,17 +322,17 @@ when isMainModule: import algorithm var a = @[1, 2, 3, 4, 5, 6, 7, 8, 9] - doAssert outplace(a, sort()) == sorted(a) - doAssert a.outplace(sort()) == sorted(a) + doAssert dup(a, sort(_)) == sorted(a) + doAssert a.dup(sort) == sorted(a) #Chaining: var aCopy = a aCopy.insert(10) - doAssert a.outplace(insert(10)).outplace(sort()) == sorted(aCopy) + doAssert a.dup(insert(10)).dup(sort()) == sorted(aCopy) import random const b = @[0, 1, 2] - let c = b.outplace shuffle() + let c = b.dup shuffle() doAssert c[0] == 1 doAssert c[1] == 0 diff --git a/lib/std/private/underscored_calls.nim b/lib/std/private/underscored_calls.nim new file mode 100644 index 000000000..7db25c410 --- /dev/null +++ b/lib/std/private/underscored_calls.nim @@ -0,0 +1,38 @@ + +# +# +# Nim's Runtime Library +# (c) Copyright 2020 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This is an internal helper module. Do not use. + +import macros + +proc underscoredCall*(n, arg0: NimNode): NimNode = + proc underscorePos(n: NimNode): int = + for i in 1 ..< n.len: + if n[i].eqIdent("_"): return i + return -1 + + if n.kind in nnkCallKinds: + result = copyNimNode(n) + result.add n[0] + + let u = underscorePos(n) + if u < 0: + result.add arg0 + for i in 1..n.len-1: result.add n[i] + else: + for i in 1..u-1: result.add n[i] + result.add arg0 + for i in u+1..n.len-1: result.add n[i] + else: + # handle e.g. 'x.dup(sort)' + result = newNimNode(nnkCall, n) + result.add n + result.add arg0 + diff --git a/lib/std/with.nim b/lib/std/with.nim new file mode 100644 index 000000000..1dfb67c2e --- /dev/null +++ b/lib/std/with.nim @@ -0,0 +1,58 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2020 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module implements the ``with`` macro for easy +## function chaining. See https://github.com/nim-lang/RFCs/issues/193 +## and https://github.com/nim-lang/RFCs/issues/192 for details leading to this +## particular design. +## +## **Since** version 1.2. + +import macros, private / underscored_calls + +macro with*(arg: typed; calls: varargs[untyped]): untyped = + ## This macro provides the `chaining`:idx: of function calls. + ## It does so by patching every call in `calls` to + ## use `arg` as the first argument. + ## **This evaluates `arg` multiple times!** + runnableExamples: + var x = "yay" + with x: + add "abc" + add "efg" + doAssert x == "yayabcefg" + + var a = 44 + with a: + += 4 + -= 5 + doAssert a == 43 + + result = newNimNode(nnkStmtList, arg) + expectKind calls, nnkArgList + let body = + if calls.len == 1 and calls[0].kind in {nnkStmtList, nnkStmtListExpr}: + calls[0] + else: + calls + for call in body: + result.add underscoredCall(call, arg) + +when isMainModule: + type + Foo = object + col, pos: string + + proc setColor(f: var Foo; r, g, b: int) = f.col = $(r, g, b) + proc setPosition(f: var Foo; x, y: float) = f.pos = $(x, y) + + var f: Foo + with(f, setColor(2, 3, 4), setPosition(0.0, 1.0)) + echo f + |