I think there are 2 kinds of immutability:

  1. Strong immutability
    * If a object is referred by a immutable reference, it never change.

(According to Rust's the book, "In Rust, the compiler guarantees that when you 
state that a value won’t change, it really won’t change.")

  2. Weak immutability (per symbol immutability)
    * Modifying a object using a immutable reference is error.
    * A object referred by immutable reference can be modified by using mutable 
reference.

(Const reference in C++ is this. You can change the value of what const 
reference refer without using pointer)




Immutability in Nim seems compiler forbids changing value using an immutable 
symbol but it doesn't guarantee the value never change.

Following code can be compiled without error. 
    
    
    proc foo(imu: string; mut: var string) =
      echo imu
      mut[0] = 'a'
      echo imu
    
    var text = "hello"
    
    foo(text, text)
    
    
    Run

Output: 
    
    
    hello
    aello
    
    
    Run

When `ref` is used, multiple `ref` symbols can refer 1 object. `ref` can refer 
a object indirectly like: 
    
    
    var
      n:Node = initNode()
      x:Node = n
      y:Node = initNode()
    y.le = n
    
    
    Run

Perfect strong immutability would be like if a object is referred by at least 1 
immutable reference directly or indirectly, modifying it is compiler error. A 
object can be modified when it is referred only by mutable symbols.
    
    
    proc addData(x: mut Node; y: Node) =
      x.data.add(y.data)
    
    var n:Node = initNode()
    addData(n, n) #Compile error because x[] is referred by y and y is 
immutable.
    var m:Node = initNode()
    m.le = n
    addData(n, m) #Compile error because x[] is referred by y.le and y is 
immutable.
    m.le = nil
    addData(n, m) #No compile error. x[] is not referred by any immutable 
symbols.
    
    
    Run

But which symbol refer which object is determined at runtime and perfect strong 
immutability cannot be achieved at compile time. (It might be possible at 
runtime if each object have a counter that counts number of immutable 
references that refering the object. But I don't think it is what Nim user 
wants.) For example: 
    
    
    proc select(a, b: Node): Node =
      result = if oracle(): a else: b
    
    proc addData(x: mut Node; y: Node) =
      x.data.add(y.data) #Compile error if y == x but not if y != x?
    
    var
      n:Node = initNode()
      m:Node = initNode()
    addData(n, select(n,m))
    
    
    Run

So making strong immutability at compile-time could be like conservatively 
forbid any changing of objects if they can be referred by any immutable 
reference. Then, `proc addData(x: mut Node; y: Node)` is always compile error. 
Any procedures that have at least 2 same type parameters and one of them is 
immutable and other is `mut` result in compile error. People have to use `mut` 
parameters even if they are supposed to be not modified. 
    
    
    proc replace(n, sub, by:mut Node):Node =
      ## If ``sub`` is subtree of ``n``, replace it with ``by``.
      ## ``sub`` and ``by`` are supposed to be not modified but compiler don't 
think so.
    
    
    Run

Conservative strong immutability seems like hard to write code or every 
parameters become `mut`.

Strong immutability might be possible without any runtime check/cost by copying 
rust's ownership, borrowing and references. 
[https://doc.rust-lang.org/book/ch04-00-understanding-ownership.html](https://doc.rust-lang.org/book/ch04-00-understanding-ownership.html)

> At any given time, you can have either one mutable reference or any number of 
> immutable references.
> 
> References must always be valid.

You cannot have a mutable reference while you have an immutable one. And you 
cannot have multiple mutable references. So you will get compile-error from 
this code: 
    
    
    proc addData(x: mut Node; y: Node)
    
    var
      n:Node = initNode()
    addData(n, n)
    
    
    Run

Per symbol immutability only forbid modification using immutable symbols. 
Objects referred by immutable symbols could be changed using mutable symbols. 
Per symbol deep immutability would be like compiler forbid changing any fields 
of immutable symbols. It still partially protect people from setting value to 
wrong variable and procedure signature can tell which parameters supposed to 
modify a object or not. 
    
    
    proc foo(imu: Node; mu: Node) =
      imu.data = "foo"   #Error
      imu.le.data = "foo" #Error
      mu.data = "foo"    #OK, even if imu == mu
      mu.le.data = "foo" #OK, even if imu.le == mu
    
    #Both harmless and harmful are vaild.
    proc harmless(a, b: Node) =
      var x = construct(a, b)
      x.data = "mutated"
    
    proc harmful(a, b: Node) =
      var x = select(a, b)
      x.data = "mutated"
    
    
    Run

If you want strong immutability, copying rust's rule could be a goot option 
unless there is better idea. But some people might think it is complicated or 
don't like it. I think per symbol immutability is simple and it is still better 
than there is no way to make `ref` immutable. 

Reply via email to