Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

How I would summarize the article is that mutation should be confined to internal objects that have not been published yet or are temporaries that are thrown away.

Theoretically, you can make every API mutate data. If the user didn't want to mutate data, they can explicitly make a copy first. Whereas if there only exists an immutable API to return new objects, then it's hard or impossible to get the benefits of mutating in place.

As far as I know, only Rust, C++, and C give support at the type-checking level to denote which functions will modify the interior of their arguments. This way, you can distinguish behaviors easily - for example in Rust:

  impl Vector {
      // The method reads this current vector and returns a new one.
      pub fn normalize(&self) -> Vector { ... }
      
      // The method reads and modifies this current vector in place.
      pub fn normalize(&mut self) { ... }
  }
The article gave this subtly flawed example in Python:

  def normalize_in_place(array: numpy.ndarray):
      low = array.min()
      high = array.max()
      array -= low
      array /= high - low
    
  def visualize(array: numpy.ndarray):
      normalize_in_place(array)
      plot_graph(array)
  
  data = generate_data()
  if DEBUG_MODE:
      visualize(data)
  do_something(data)
In Rust, it would be a type error because visualize() takes a reference but normalize_in_place() takes a mutable reference:

  fn normalize_in_place(array: &mut numpy::ndarray) {
      let low = array.min();
      let high = array.max();
      array -= low;
      array /= high - low;
  }
  
  fn visualize(array: &numpy::ndarray) {
      normalize_in_place(array);  // ERROR
      plot_graph(array);
  }
  
  let data: numpy::ndarray = generate_data();
  if DEBUG_MODE {
    visualize(&data);
  }
  do_something(&data);


> As far as I know, only Rust, C++, and C give support at the type-checking level to denote which functions will modify the interior of their arguments.

1. C and C++ allow casting away constness, which may or may not be UB depending how the parameter is defined

2. Swift also provides this ability to an extent: struct parameters have to be flagged `inout` to be mutable

I'm sure there are others.


Rust can throw away constness in unsafe blocks, which carries the same caveat emptor that const_cast carries.


Only in certain circumstances, though.


Casting away constness in C++ is something it is virtually never done, and has to be done very explicitly.


> https://github.com/search?q=const_cast&type=Code

We must have very different notions of "virtually never": https://github.com/search?q=const_cast&type=Code

> has to be done very explicitly.

That doesn't make it visible to the caller.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: