-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[WIP] [RFC] Nim should have immutable
type qualifier (let
is more like C++'s const); it would enable a new string class design, etc
#8370
Comments
immutable
storage class (let is more like C++'s const); useful for a new string class design, etcimmutable
storage class (let
is more like C++'s const); useful for a new string class design, etc
immutable
storage class (let
is more like C++'s const); useful for a new string class design, etcimmutable
storage class (let
is more like C++'s const); it would enable a new string class design, etc
immutable
storage class (let
is more like C++'s const); it would enable a new string class design, etcimmutable
type qualifier (let
is more like C++'s const); it would enable a new string class design, etc
Deep immutability can eventually be done with my https://nim-lang.org/araq/writetracking.html "Write tracking" idea which works much better than encoding this in the type system. It still has its problems though. |
(deep immutability can be done by declaring all fields private, and exporting only read accessors, recursively, although this is inconvenient) |
I really wish something like #6890 would be implemented, which would make this easier. |
Linked to #6793 - Return by let/const value (Apply parameter constraints to return values) Quote:
|
I am pretty sure somewhere in the issues there is a long discussion between me and @Araq where I am confused by the fact that the presence of a pointer invalidates the guarantees of immutability, so I am with you on this. That said, I am against yet another language feature. One compromise would be a macro in |
(The discussion is here https://github.com/nim-lang/Nim/issues/5753 ) |
I started to use C++ before const-madness and I don't like this C++ feature. Once a language is serious about const-ness propagation, it's a plague that affects all your code. You can't have And all that mess just to detect some type of errors. It's the Rust way - complicate every line of program just to prevent one type of bugs. It's why I prefer idea of effects system - if it can catch some errors without complicating the code, it will be OK for me. Without language changes, you can implement ImmutableString and ImmutableArray types and ImmutableSpan to operate on their parts. What you think about this approach? In particular, it will allow to accumulate real experience with immutable data types before going with |
@Bulat-Ziganshin there is no const-madness in Nim.
For pure value types, I agree with the goal, having asked this several times to allow move/copy elision in my tensor library (copying 8GB of GPU memory is a big no-no). Also, we shouldn't use char pointer as a comparison point, Nim strings are very different from C++ strings, Nim already tracks constness of strings and seq properly. There is this short study on mutability semantics, seems like even Ocaml (that uses |
@mratsim Scala has different approach. It uses class Mutable(var x: Int)
class Immutable(val x: Int)
val x1 = Mutable(0) // immutable reference to a mutable object
var x2 = Mutable(0) // mutable reference to a mutable object
val x3 = Immutable(0) // immutable reference to an immutable object
var x4 = Immutable(0) // mutable reference to an immutable object
// these all compile
x1.x = 1
x2.x = 1
x2 = Mutable(2)
x4 = Immutable(1)
// these fail to compile
x1 = Mutable(1)
x3 = Immutable(1)
x3.x = 1
x4.x = 1 |
yes, and I think that this proposal will add it Why Now, if you want to enforce rule Moreover, some of these functions will have different implementation depending on parameter immutability (substr is the best example), so you will need to provide overloading for both cases. But what about mutable collections of immutable things? If they are prohibited, it will seriously limit let-vars usability. Just imagine - you can't assign them to fields of mutable objects. So, we quickly going to idea that const-ness is just specifier belonging to each level of ref/ptr. And now you can't just declare function parameter as 'immutable' - you need to go through its entire structure and mark each ptr/ref as immutable or not. Otherwise the type system will bite you, either allowing to modify thing supposed to be immutable, or opposite. And now, instead of simple And we don't yet looked into generic definitions and lambdas. Overall, look into C++ textbooks. It looks like a great idea until you open this can of worms. |
There is no madness in Nim precisely because mutability in Nim is an aspect of the symbol, not of the type. It works out beautifully and with the coming move semantics |
https://nim-lang.org/araq/writetracking.html explains it all. |
I read through https://nim-lang.org/araq/writetracking.html ; I need to think more about it to have an informed opinion; one thing though:
this is C++ specific, and doesn't apply to D, where the following works fine: import std.stdio;
alias V1 = const(string)[];
alias V2 = string[];
void fun1(V1 a){
//a[0] = "HELLO"; // would error (as expected)
writeln("a:", a);
}
void fun2(V2 a){
a[0] = "HELLO";
writeln("a:", a);
}
void main(){
V2 a2 = ["hello", "world"];
V1 a1=a2; // ok
fun1(a2); // ok
fun2(a2); // ok
} could you provide a good motivating example where D's approach to
acknowledged, this does apply to D.
despite all of D's problems, I don't think const-madness is one of them; at least not a serious one that people complain about (see D survey).
|
Slices to immutable data require either RC-like operations to ensure temporal memory safety or a tracing GC mechanism (or a Rust-like borrow checker, but that's more limited). Not something we should embrace too quickly. |
Well, not gonna happen anytime soon, but I encourage you to spend some time with my writetracking algorithm. |
immutable
concept that denotes data that cannot be modified through any reference (see example 1 to showlet
doesn't satisfy this stronger guarantee)let a=bar
is shallow, not deep, see see example 2.So
let a=bar
is more likeconst
in C++ (orconst
in D, except that D's const is deep)See also https://dlang.org/spec/const3.html for distinction between D's const and immutable, or Example of const vs. immutable
This lack of type safe causes bugs, eg:
Writing to cstring, SIGBUS error
#8463 (this would be a compile error with immutable strings, as is case in D)Nim docs are misleading or at least imprecise regarding meaning of
let
:doc/tut1.rst
and:
and, in error messages involving a
let
variable:however, this immutability guarantee isn't true as example below shows:
let
is more like C++const
let
is shallow, not deepwhy do we need type qualifiers?
mutable array of immutable int
data vsimmutable array of int
NOTE: for
Slice
, a possible implementation is Range from https://github.com/status-im/nim-rangesThis concept is very useful in things like containers, eg:
why do we need
immutable
concept ?for i in 0..10: let a="foo"
which allocates 10 times, see https://forum.nim-lang.org/t/4060#25287; it also prevents efficient slicing of strings which makes a lot of code inefficient and has deep consequences in terms of library design as library authors have to resort to workarounds [4]The design I (and probably others) have in mind (can do a writeup later) would be closely modeled after D's
string
design which defines a string asalias string = immutable(char)[];
, mutable strings aschar[]
, all of which are just special cases of dynamic arraysT[]
. This would enable safe and efficient string handling.NOTE: we'd exclude some bad decisions in D's standard library, eg auto-decoding of UTF8 strings (and all it's associated bad consequences: ForEachType vs ElementType, and all string special casing in std.algorithm)
proposal: type qualifiers
rconst
andimmut
Currently we have:
const a=1 # a is compile time constant
var a=1 # a is "int" (aka mutable)
let a=1 # a is "rconst(int)"
NOTE: can't call this "immutable(int)" as this isn't the case; can't call it
const(int)
asconst
already means "compile time const"; sorconst
can be used, meaning run-time constwe introduce type qualifiers
immut
as well asimmut a = ...
syntax:immut a = 1 # a is immut(int)
Example:
NOTE: as for
shallow
vsdeep
, I need to think more on what would be the best, there are pros and cons.footnotes
\0
start = 0
parameters inlib/pure/parseutils.nim
complicate the code and would not be needed if using slicesThe text was updated successfully, but these errors were encountered: