Discovering Nim, part 1

Posted on Fri 02 December 2022 in Coding

For this year’s Advent of Code, I use the programming language Nim. I wrote a little bit about Advent of Code yesterday.

For today’s puzzle, I got stuck on something that normally is so trivial that you don’t think twice about it—accessing a variable in the outer scope from a function. Suppose we have the following code, which is a very simplified version of the situation I was in:

var i = 0

proc inc(n: int): int =
    i += n
    return i

proc main() =
    const x = inc(5)
    echo x

main()

Anyone who codes Nim will immediately spot the error, of course, but it eluded me for quite a while, even though the compiler was very clear:

test.nim(4, 5) Error: cannot evaluate at compile time: i

I found this to be a little bit strange—why would the compiler try to evaluate i at compile time? The access is clearly in a proc, which gets called at runtime, right? Instead of taking a moment to think a little bit about this, I figured it had to do with the declaration of i—that I somehow had to tell Nim that access to the outer-scope variable was ok, because of some odd scope rules that I hadn’t learned about yet. I believe this belongs to the “I’m doing things right, clearly the compiler must be wrong” phase of troubleshooting!

No, the problem was this line:

const x = inc(5)

By using const, the inc function was actually evaluated by the compiler as a constant expression, and a constant expression, being evaluated at compile time, cannot use a runtime variable. Which the compiler tried to tell me, but I wasn’t listening!

This is a silly mistake that I attribute to coding in multiple different languages. I write quite a lot of TypeScript, where const is used for constants, let is used for block-scoped variables, and var is used for function-scoped variables. In Nim, const is used for compile-time constants, let for “single assignment” runtime variables, and var for “multiple assignment” variables. Clearly, my mind wasn’t fully in “Nim land,” so my fingers typed const on auto pilot, and since it wasn’t a concious choice, I didn’t connect it to the error.

This reminds me of when I took both French and Spanish at the same time in school. In one class, I accidentally mixed the languages when counting (un, deux, tres…). My French teacher was NOT happy about it, much like the Nim compiler!