why is this allowed
proc g(x: int): auto = result = g(x)
but not this
let g = proc (x: int): auto = result = g(x)
Error: undeclared identifier: 'g'
To elaborate on Araq's answer, in Nim like in many other programming languages such as C++ or Java there is a fundamental difference between these two cases.
Your first example shows a regularily defined procedure while the latter does two things at once. It defines a variable and assigns to it a reference to an annonymous procedure.
Since the variable is only declared after evaluating the expression to be assign to it recursion is not possible.
And this is not the only issue or limitations, for example they cannot participate in procedure overloading (since you cannot have two variables with the same name in the same scope) or dot call syntax. It also gives the compiler less optimisation opportunities since you are dealing with function references now.
even mid-expression
well you can define procs anywhere you can define vars. like:
let x =
if false: 0
else:
proc foo(x: int): int = x*2 + 1
foo(3)
or
type Foo = object
x: int
let foo = Foo(
x: block:
proc factorial(i: int): int =
if i <= 1: 1
else: i * factorial(i-1)
factorial(5)
)
I found out about the sugar package but it still doesn't allow
the => macro just gives syntactic sugar for defining anonymous procs, so your => version gets extended to the exact same thing
Is there a way around this? This really hampers functional programming styles where you make short lambda functions all the time, even mid-expression.
So use the Y-combinator.
var g: proc(x: int): int
g = proc (x: int): int = g(x)
But it's unknown if a closure can capture itself. Or maybe
let g = block:
proc f(x: int): int = f(x)
f
Also, I think it cannot be done with auto, which introduces an implicit generic parameter and thus not suitable for variable type. import sugar
let f = (x: int) -> int => x * x
let g = proc(x: int): int = x * x
let m = (x: int, y: (int) -> int) => y(x)
echo f(100)
echo g(100)
echo m(100,f)
echo m(100,(x: int) => x * x)
you can write recursive, self-capturing lambdas if you name them first
import sugar
var fib:int->int
fib = (x:int) => (if x < 2 : x else: fib(x-1) + fib(x-2))
echo block: collect:
for i in 0..10:
fib(i)
to get gcc to tail-call any recursive proc you need to write it in an awkward way, making sure the last statement is the recursive call.
but doing so with a closure or a proc results in tail-call optimization, at least for this toy example: https://godbolt.org/z/9s5b3Tzb5
giant YMMV applies, obviously, but interesting i thought.