From now on I will use term pointer both for not-null pointers and possible-null pointers. Just to avoid confusion with the mechanism I describe below.
You can declare a function parameter value with 3 types of constraints:
def foo(x MyClass)
is shorter form of:
def foo(val x MyClass)
The argument is passed by value and you have a guarantee you cannot change “x
” inside “foo
”. If you need speed and you know you work in single-threaded environment you could use a shortcut (both forms are shown):
def foo(ptr x MyClass)
def foo(ptr val x MyClass)
The argument is passed by pointer (fast), but since compiler guarantees the data is not changed inside a function, the only worry is whether the callee continues to work or not.
The second type:
def foo(var x MyClass)
The execution is slow (value is copied), you can change the value inside the function but the alterations do not leak outside.
And third one:
def foo(ref x MyClass)
Execution is fast and not only you can change the value inside, but any change is also visible outside the function (argument is passed via reference). However because a callee is affected you have to pass the argument consciously:
foo(ref x);
All the checking is done during compile time. However one constraint is missing — declaring a function which takes constant and forces callee it is constant as well. Too academic?
Consider creating a dictionary (a collection that maps keys on values). For fast retrieval usually a dictionary orders keys in some way — the algorithm is not important, all we know that keys are kept in some order, and dictionary uses this fact to skip a good portion of the keys when doing lookup. If you create dictionary and pass initial data, and then you change the keys on a callee side, the dictionary will not know about the change, and it won’t reorder the keys. Thus on next retrieval you can expect anything, which translates to “undefined behaviour”.
One way to solve this problem would be cloning all the keys, but this is inefficient and kills any attempt to optimize the algorithm (once you embed cloning in the dictionary you cannot get rid of it from the outside).
So far I see two ways to solve this — either add another constraint:
def foo(const x MyClass)
Meaning — I cannot change “x
” inside, and I also require “x
” is constant outside. In other words, the value has to be generally constant, not just in local scope. Since it is const two ways it can by passed silently via pointer (fast); note that passing the data explicitly by pointer (“ptr val
”) does not give a guarantee on true constness.
Or go Ruby way and provide a method ”freeze
” that would lock object against writing. I prefer the first approach (enforced during compilation) but I am not sure whether this is doable, it looks suspiciously tempting.
I like the idea of setting constraints on both sides, however even in this post I already see too rich syntax. Those constraints are much better if they are triggered automatically — for example knowing the call is done synchronously or not (for 100%) could allow automatic optimization how to pass data efficiently, by pointer or by value.