Hello, Nim Community!
Few days ago I stumbled upon the Nim Programming Language and quickly have fallen in love with it. It is beautiful and powerful! Mostly by trial and error I figured out a way of defining named closures using block scope. But I am not sure that my approach resulted in idiomatic Nim. May be a better or/and simpler alternatives exist that evaded the beginner's attention. Anyway, here is my code:
## Define a single closure within the block
let hello = block:
let greeting= "Hey, "
(proc (name: string): string =
result= greeting & name & "!"
)
doAssert hello("Nim") == "Hey, Nim!"
In a similar way one can define several closures that share the same environment
## Define two closures sharing the same environment within the block
let (hello, set_greeting) = block:
var greeting= "Hey, "
proc hello(name: string): string =
result= greeting & name & "!"
proc set_greeting(new_greeting: string): string =
result = greeting
greeting= new_greeting
(hello, set_greeting)
doAssert hello("Nim") == "Hey, Nim!"
doAssert set_greeting("HELLO, ") == "Hey, "
doAssert hello("NIM") == "HELLO, NIM!"
Any comments & critique are highly appreciated!
Thanks,
Hi,
Have you taken a look at the Nim manual ? It has a chapter on Closure https://nim-lang.org/docs/manual.html#procedures-closures.
Personnaly, my favorite "most idiomatic" way of dealing with closure in Nim is through the sugar module : https://nim-lang.org/docs/sugar.html
This allows you to write closure using => : For example (taken shamelessly from the documentation) :
proc passTwoAndTwo(f: (int, int) -> int): int =
f(2, 2)
passTwoAndTwo((x, y) => x + y)
I find it especially useful when working with seq, you can do stuff like this :
import sugar
import sequtils
var myseq = @[1, 2, 3, 4, 5, 6]
myseq.apply(x => x+1)
echo myseq
# Dividing a seq[int]will only give us @[0, 0, 0, ...]
# Using map(x => ...) instead of appyl create another seq
var otherseq = myseq.map(x => x/12)
echo otherseq
echo type(myseq)
echo type(otherseq)
@Clonk Thanks for the pointer to the closure section in the manual. As of now, I have been a Nim user for the whole 3 days and have not read the manual in its entirety yet :-)
Closure syntax in sugar module is interesting. But my question is about semantics. I am creating closures from within a block scope and make them accessible on the top level. Within the block an environment encapsulates some global state that is only accessible via these closures. I effectively "export" inner-scope closures to the top level.
What do you think about using the template?
template defSayText(text: string): untyped =
proc sayText() =
echo text
sayText
template defSetText(text: string): untyped =
proc setText(newText: string) =
text = newText
setText
let (sayText, setText) = block:
var text = "Hello Nim!"
(
defSayText(text),
defSetText(text)
)
sayText() # prints "Hello Nim!"
setText("Hello world!")
sayText() # prints "Hello world!"
Or returning a closure from a proc
proc closeness(str:string):proc(s:string):string=
let greeting = str
return proc(s:string):string = greeting & s & '!'
let hello = closeness("Hello, ")
let hey = closeness("Hey, ")
assert hello("Nim") == "Hello, Nim!"
assert hey("there") == "Hey, there!"
But blocks are brilliant, I didn't get them at first now use them all the time