Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

new outplace operator: doAssert @[2,1,3].>sort() == @[1,2,3] #13309

Closed
wants to merge 11 commits into from
7 changes: 4 additions & 3 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,6 @@
- introduced new procs in `tables.nim`: `OrderedTable.pop`, `CountTable.del`,
`CountTable.pop`, `Table.pop`
- To `strtabs.nim`, added `StringTable.clear` overload that reuses the existing mode.
- Added `sugar.outplace` 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.
- Added `sugar.capture` for capturing some local loop variables when creating a closure.
This is an enhanced version of `closureScope`.
Expand All @@ -54,6 +51,10 @@
- Added `minIndex` and `maxIndex` to the `sequtils` module
- Added `os.isRelativeTo` to tell whether a path is relative to another
- Added `resetOutputFormatters` to `unittest`
- Added new `outplaces` module with `.>` outplace operator: `@[2,1,3].>sort()`.
It turn 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. This avoids duplicating API's with
outplace variants.

## Library changes

Expand Down
43 changes: 0 additions & 43 deletions lib/pure/sugar.nim
Original file line number Diff line number Diff line change
Expand Up @@ -189,32 +189,6 @@ macro capture*(locals: openArray[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)

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...
case n.kind
Expand Down Expand Up @@ -307,23 +281,6 @@ macro collect*(init, body: untyped): untyped {.since: (1, 1).} =

when isMainModule:
since (1, 1):
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)
#Chaining:
var aCopy = a
aCopy.insert(10)
doAssert a.outplace(insert(10)).outplace(sort()) == sorted(aCopy)

import random

const b = @[0, 1, 2]
let c = b.outplace shuffle()
doAssert c[0] == 1
doAssert c[1] == 0

#test collect
import sets, tables

Expand Down
47 changes: 47 additions & 0 deletions lib/std/outplaces.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import std/macros
include system/inclrtl

proc outplaceImpl(arg, call: NimNode): NimNode =
expectKind call, nnkCallKinds
let tmp = genSym(nskVar, "outplaceResult")
var callsons = call[0..^1]
var found = false
for i in 1..<len(callsons):
if callsons[i].kind == nnkIdent and callsons[i].strVal == "_":
callsons[i] = tmp
found = true
break
if not found: callsons.insert(tmp, 1)
result = newTree(nnkStmtListExpr,
newVarStmt(tmp, arg),
copyNimNode(call).add callsons,
tmp)

proc replaceOutplace(lhs, n: NimNode): NimNode =
case n.kind
of nnkDotExpr, nnkBracketExpr:
result = copyNimTree(n)
result[0] = replaceOutplace(lhs, result[0])
of nnkCall:
result = outplaceImpl(lhs, n)
of nnkCommand:
result = outplaceImpl(lhs, n)
else:
doAssert false, "unexpected kind: " & $n.kind

macro `.>`*(lhs, rhs): untyped {.since: (1, 1).} =
## Outplace operator: turns an `in-place`:idx: algorithm into one that works on
## a copy and returns this copy. A placeholder `_` can optionally be used to
## specify an output parameter of position > 0.
##
## **Since**: Version 1.2.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Paragraphs! Please add empty line above this, otherwise it'll render poorly in the doc gen.

Also, you need to add this file to the docgen config IIRC.

Copy link
Member Author

@timotheecour timotheecour Feb 4, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add empty line above this

done

Also, you need to add this file to the docgen config IIRC.

I believe I've fixed this in a general way in #13221

runnableExamples:
import algorithm, strutils
doAssert @[2,1,3].>sort() == @[1,2,3]
doAssert "".>addQuoted("foX").toUpper == "\"FOX\""
doAssert "A".>addQuoted("foo").toUpper[0..1].toLower == "a\""
proc bar(x: int, ret: var int) = ret += x
doAssert 3.>bar(4, _) == 3 + 4 # use placeholder `_` to specify a position > 0
doAssert @[2,1,3].>sort(_) == @[1,2,3] # `_` works but unneeded in position 0
result = copyNimTree(rhs)
result = replaceOutplace(lhs, result)
31 changes: 31 additions & 0 deletions tests/stdlib/toutplaces.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import std/[outplaces, algorithm, random, os]

proc main() =
var a = @[1, 2, 3, 4, 5, 6, 7, 8, 9]
doAssert a.>sort() == sorted(a)
#Chaining:
var aCopy = a
aCopy.insert(10)
doAssert a.>insert(10).>sort() == sorted(aCopy)
doAssert @[1,3,2].>sort().>sort(order = SortOrder.Descending) == @[3,2,1]
# using 2 `.>` chained together

proc bar(x: int, ret: var int) = ret += x
doAssert 3.>bar(4, _) == 3 + 4

const b = @[0, 1, 2]
let c = b.>shuffle()
doAssert c[0] == 1
doAssert c[1] == 0

block:
var a = "foo"
var b = "bar"
doAssert "ab".>add("cd") == "abcd"
let ret = "ab".>add "cd" # example with `nnkCommand`
doAssert ret == "abcd"

when defined(posix):
doAssert "foo./bar///".>normalizePathEnd() == "foo./bar"

main()