Like sortIt() or applyIt() where we are forced to use the it variable name in the condition?

When I started with Nim some years ago I thought that that is the only possible way to do it in Nim -- but I always regard it as very ugly.

I just remembered that I tried successfully to use a custom variable name some time ago. I think it was something like this:

#The original Nim version:
template applyIt*(varSeq, op: untyped) =
  for i in 0 .. <varSeq.len:
    let it {.inject.} = varSeq[i]
    varSeq[i] = op

var nums = @[1, 2, 3, 4]
nums.applyIt(it * 3)
assert nums[0] + nums[3] == 15

# the new version with custom name
template apply*(varSeq, el, op: untyped) =
  for el in mitems(varSeq):
    el = op

nums = @[1, 2, 3, 4]
nums.apply(el, el * 3)
assert nums[0] + nums[3] == 15
echo nums

Personally I regard the call nums.apply(el, el * 3) as much nicer. It looks more like the Ruby code, where we have for example

a = [ "a", "b", "c" ]
a.delete_if {|x| x >= "b" }   #=> ["a"]

I have not yet investigated if other It templates like sortIt() can be rewritten in this way too, but I hope so.

2017-11-10 20:59:01
I like them, they make the code even shorter and that's what "lambdas" are all about. I don't really need the "flexibility" of using a, b, x, y, z here instead but I suppose the ability to nest these suffers.
2017-11-10 22:01:41

I'm with Stefan here, those it seem to be dropped from nowhere and appear and disappear like magic.

For me lambdas are about reducing boilerplate yes, but also readability and composition.

It was non-trivial to understand where did the it in mapIt or the a and b in fold came from at first. Templates seemed like magic.

2017-11-10 23:01:24
I like Stefan_Salewski's version as well. It reminds me of python generator expressions. 2017-11-11 03:03:38

If you are using an untyped parameter in a macro, try the for construct:

mapIt(somemap, for e in pow(e, 2))

You're already used to reading "for a value which shall change at each step" so only the "in an expression" is being mentally overloaded. It's as close as we're going to get to Ruby without more syntax.

2017-11-11 04:42:06

@Stefan_Salewski If you want something Ruby-like, why not use => from future?

Like this:

import future, sequtils

var nums = @[1, 2, 3, 4]

nums.apply(el => el * 3)
echo nums

2017-11-11 05:57:27

@olwi, that creates a closure, if you use that in a loop it will be significantly slower than an inline template. See my bench in Arraymancer.

# Results with -d:release on i5-5257U (dual-core mobile 2.7GHz, turbo 3.1)
# Proc with ref object 0.099993
# Closures 2.708598
# Methods 0.3122219999999998

2017-11-11 06:24:44
sequtils could use some overloads to accept this behavior witouth breaking the old one:
template apply*(varSeq, op: untyped) = apply(varSeq, it, op)
2017-11-11 10:15:40
I really like ~It templates, I consider them as one of the favourite Nim's features. They efficient as hell (lambda and closures no match here) and they even shorter to write and easy to read as well. More readible then Ruby in my opinion. I have several ~It templates in my arsenal as well. They only thing I can agree with, is when we have more then one argument (map, fold) "a,b" then it is confusing which one is which.
2017-11-11 18:23:26

I tried this for benchmarking (edit: the web server doesn't do -d:release), but on my machine, they are all equivalement, just change due to the GC timings

import future, sequtils, times, math

const
  LoopCnt = 10_000_000
  mult = 1000

var
  sq: seq[float] = newSeqWith(LoopCnt, 0.0)
  t0 = epochTime()

proc setupSeq() =
  for i in 0..<LoopCnt: sq[i] = i.float

proc useIt() =
  setupSeq()
  t0 = epochTime()
  sq.applyIt(pow(it,2))
  echo "applyIt: ", mult * (epochTime() - t0)

proc useClosure() =
  setupSeq()
  t0 = epochTime()
  sq.apply(x => pow(x,2))
  echo "closure: ", mult * (epochTime() - t0)

proc useLoop1() =
  setupSeq()
  t0 = epochTime()
  for i in 0..<LoopCnt: sq[i] = pow(sq[i], 2)
  echo "loop1: ", mult * (epochTime() - t0)

proc useLoop2() =
  setupSeq()
  t0 = epochTime()
  for i in 0..<LoopCnt: sq[i] = sq[i] * sq[i]
  echo "loop2: ", mult * (epochTime() - t0)

proc main() =
  useIt()
  useIt()
  useClosure()
  useClosure()
  useLoop1()
  useLoop1()
  useLoop2()
  useLoop2()

main()

2017-11-11 21:29:28
<<<••12••>>>