2

Semantics of let assignment in Nim

I was recommended the Nim programming language and started reading through the Tutorial 1, the Manual and even the memory model (in particular the section about strings and seqs).

I soon encountered some strange behavior regarding let assignment which doesn't seem to make any sense at all, to the point of appearing a complete mess. I can demonstrate all the nonsense with this simple script (link to playground):

echo "first example:"

proc p =
  var msg = "Hello"
  let copy = msg
  msg.add(" World!")
  echo msg
  echo copy # does NOT change
p()

echo "\nsecond example:"

proc q =
  var msg = "Hello"
  let copy = msg
  msg.add("!")
  echo msg
  echo copy # DOES change
q()

echo "\nthird example:"

var msg = "Hello"
let copy = msg
msg.add("!")
echo msg
echo copy # does NOT change

In the first and third examples, copy retains the original value "Hello". The second example (proc q) shares a similarity with the other two cases:

  • it is similar to the first because it employs the local scope of a proc,
  • it is similar to the third because it appends the same string ("!").

For some arcane reason however, in the first and third examples copy is indeed a copy (bind by value?), whereas in the second example the modification to the variable msg propagates to the immutable copy (bind by reference?).

What is going on? How can we make sense of this? Does let bind by value or by reference? If it make sense somehow, why is it not explained clearly and in depth in the official guides?

Federico
  • 582
  • 3
  • 12
  • 1
    This is quite likely related : https://stackoverflow.com/questions/73940369/nim-sequence-assignment-value-semantics – Stefan Zobel Jul 20 '23 at 21:15
  • @StefanZobel Oh my God you are right, IT IS A MESS indeed... Ok, I will immediately steer clear of a language that hasn't even made up its mind about the exact rules of binding identifiers – Federico Jul 21 '23 at 08:36

2 Answers2

2

This happens if you append and the resulting string is less than 8 bytes. I. e. if you append to var msg="1" less than 7 chars, the copied let copy = msg also gets modified. Appending 7 or more and the two strings are different:

proc q =
  var original = "1"
  let copy = original
  original.add "2"
  echo original, " ", copy

q()

>>> 12 12

The addr of both are the same:

  echo original.repr, " ", copy.repr

>>> 0x7fa5ac992060"12" 0x7fa5ac992060"12"

But if you add characters enough that the final original string gets to 8 bits or more:

proc q =
  var original = "1"
  let copy = original
  original.add "234567"

  echo original, " ", copy
  echo original.repr, " ", copy.repr

  original.add "8"

  echo original, " ", copy
  echo original.repr, " ", copy.repr

q()

>>> 1234567 1234567
>>> 0x7f2f0f5cb060"1234567" 0x7f2f0f5cb060"1234567"
>>> 12345678 1234567
>>> 0x7f2f0f5cb1b0"12345678" 0x7f2f0f5cb060"1234567"

The original string gets a new address, and the copy is still at the initial one.

xbello
  • 7,223
  • 3
  • 28
  • 41
  • 1
    Yeah, so this actually confirms that the semantics of the language is a complete mess – Federico Jul 26 '23 at 14:51
  • It looks like a bug, not an irreversible semantics problem. Similar problems were reported and fixed before. Also "complete mess" is a little exageration, given this code is a bit unrealistic. Wouldn't be a surprise if this is a side effect of a C compiler optimization. – xbello Jul 26 '23 at 18:53
1

I found strange thing:

msg.add("!") # work in procedures (1 and 2 examples, but not 3)
msg.add(" World!") # doesn't work in all examples

At top-level string variable just copied, but in procedures small changes looks like changing pointers, but big changes looks like copy ...

I'm trying to change let to var and here is not changes.

I think that reason is compiler bug.

Ethosa
  • 169
  • 7
  • 3
    I found that in the `q` proc if you add either "!" or "!!" it binds by reference and the original gets also modified. But if you add "!!!", it doesn't modify the original. – xbello Jul 21 '23 at 08:42
  • 1
    Exactly, it's a nightmare... – Federico Jul 21 '23 at 14:30