Dyslexia is a complicated issue (it is also not a single issue, but a collection of similar issues with different specific limitations and usable workarounds). I can see how var and val are very similar and perhaps hard to handle. Unfortunately, as the language is stable now, there is very little that can be done in this regard. Syntax highlighting can help, and is done also at the use site (a variable looks different from a value when referenced).
When you talk about “variable values” you are however making a semantic mistake, although the use of val does not help this. The distinction between primitives and classes does not help either. Let’s start
from the beginning: primitives.
A primitive value is something like 42
. It only has the number, but not an address in memory. If I write the number again 42
is the same value as the first occurrence. To be able to work with values it is however necessary to store them in some place (as local variable, field, in an array or otherwise). The place where you store the value is (generalizing slightly) some place in memory. To use the value stored in memory you need to have a way “know” this location. There are different ways a value ends up at a location, leading to different ways to store a location.
If the location is handled by the compiler this is what is informally called a variable, but could be a (local variable, field (instance or class), constant, or temporary (only visible to the compiler)). Note that on the JVM (like on the X86 architecture) local variables and parameters work the same way. The second way to handle locations is through pointers (or references) where the location is a value itself, and used to indirectly find the memory location of the value. (This reference needs to be stored somewhere using the same, recursive, rules).
Variables are called variables because, if you have a location in memory it is possible to change the value stored at that location (assuming no hardware protection is applied). Unfortunately changing what is stored in a location can make reasoning about that location hard (for humans and compilers) and in case of multithreading makes it very hard to handle timing (and even harder for multicore processors to do this efficiently).
Looking at things from the perspective of variables (memory locations) is not the only way to work with values. The other one comes from math (algebra) where you have no storage locations, but you do have names. Given a function f(x)=x+2
the x
in this function is a name for an unknown value. This x
only has a value for a specific invocation of f
so there are different “copies” of x
for f(4)
, f(5)
and f(42)
. This is obvious if you cannot change the value of x
in your formula, but actually this is also true with most variables - two calls of a function that has a y
variable that changes inside the function still have different locations (possibly) that have different values stored in them.
Like I said before, it is technically always necessary to have a location in memory to store a value for it to be usable by a processor. This means that even if you are purely thinking of something as a name for a specific value there is an underlying storage location. Conceptually you are however doing something quite different. So in Java a final int x=42
is an immutable variable that stores the value 42, where in Kotlin val x=42
introduces a name x for the value 42. In bytecode both are equal.
Java (inheriting from C) takes an implementation derived approach to how variables work, Kotlin takes a conceptual approach. Unfortunately this then gets muddied up with practicalities such as delegates, getters and “references”.
References are nothing more than fancy pointers. Where a pointer (a variable or value containing the address that stores another value) requires explicit usage of the address, references do this implicitly (a reference is a name for whatever is contained in the memory for which the address is held by the reference). In Java/Kotlin it is only possible to specify that the reference itself is not allowed to change (the reference will always point to the same memory). It is not possible to do this for the destination of the reference. Java/Kotlin will always use references for objects. One of the consequences is that if copying is surprising.
If you have the following:
data class A(var b:Int)
val x = A(42)
val y = x
both x
and y
will hold the address of the same object with member b==42
. If you were to run y.b=5
what happens is that field b
of the object referred to by y will be changed to hold 5
(instead of 42
) . As x
and y
refer to the same object this means that x.b
would now result in the value 5
.