June 24, 2007
Bill Baxter wrote:
> Walter Bright wrote:
>> Sean Kelly wrote:
>>> Walter Bright wrote:
>>>> Sean Kelly wrote:
>>>>> Matter of opinion, I suppose.  The C++ design was immediately clear to me, though it obviously wasn't for others.  I grant that the aliasing problem can be confusing, but I feel that it is a peripheral issue.
>>>>
>>>> I don't think it is a peripheral issue. It completely screws up optimization, is useless for threading support, and has spawned endless angst about why C++ code is slower than Fortran code.
>>>
>>> As a programmer, I consider the optimization problem to be a non-issue.    Optimization is just magic that happens to make my program run faster.
>>
>> Optimization often makes the difference between a successful project and a failure. C++ has failed to supplant FORTRAN, because although in every respect but one C++ is better, that one - optimization of arrays - matters a whole lot. It drives people using C++ to use inline assembler. They spend a lot of time on the issue. Various proposals to fix it, like 'noalias' and 'restrict', consume vast amounts of programmer time. And time is money.
> 
> FORTRAN is also helped by having a fairly standardized ABI that can be called easily from lots of languages, which C++ lacks.  But C has that, and it has also failed to supplant FORTRAN for numeric code.  But I think Sean's right.  A lot of that is just that the language supports things like actual multi-dimensional arrays (by 'actual' I mean contiguous memory rather than pointers to pointers), and mathematical operations on them right out of the box.  Telling a numerics person that C/C++ will give them much better IO and GUI support, but take them a 

But, what if you could get all of that in a language, plus the array performance of Fortran with the 'safety' of things like invariant... To Walter's point, if a language can handle arrays well (great performance), it makes a lot more sense to support things like MD arrays properly in the language, and it will make it a lot easier to justify numerical libraries written in D as well. Plus a lot of applications that never even touch the FP stack could benefit from great array performance.

D has a dilemma - because of its C lineage and semantics, it can't just ignore the aliasing issue (like Fortran does), but in order to be an improvement over C and C++ in all important aspects, it should address the issue somehow. IMHO the most logical way (since pointer/reference and data-flow analysis probably can't eliminate even a majority of the aliasing issues) is to improve on C++'s idea of 'const' and hand the semantic control over to the programmer in a way the compiler can rely on.

> step back in terms of core numerics is like trying to sell a hunter a fancy new gun with a fantastic scope that will let you pinpoint a mouse at 500 yards but -- oh, I should mention it only shoots bb's.
>
> On the other hand, I suspect there's lots of code that's written in FORTRAN supposedly for performance reasons that doesn't really need to be.  Just as there's lots of code written in C++ that would perform fine in a scripting language.  But people will still swear up and down that {whatever} is the only language fast enough.  A lot of numerics folks do realize this, however.  They just go from Fortran straight to Matlab, and skip the other compiled languages altogether.
> 
> I guess what I'd like to say in summary is that I'm skeptical about the claim that optimization "often" makes the difference between success and failure.  "occasionally" I could believe.  Ill-advised premature optimization has probably led to the demise of many more a project than actual optimization problems in the end product.  We'll all gladly take a free 20% speed improvement if the compiler can give it to us, but I don't believe there are that many projects that will fail simply for lack of that 20%.
> 

<soapbox>
In my experience this is not the case - I've worked on projects where "tuning" took up a significant (say ~20%) of the total cost even though the software nominally met the requirements, because end-users were not happy with performance. These projects probably would not have strictly "failed" because they did the job (slowly), but they would still have been "failures" in the eyes of the end-users to a significant degree. I'm sure we've all been there to one degree or another.

Some of these particular issues were taken care of by things like applying databases indexes (that are not directly related to what we're talking about here) but some of the issues involved exactly what Walter is talking about - how fast the language the application was written in could handle arrays of data. In one for example, we had to settle on single precision FP, even though double precision met the requirements better, in order to mitigate performance complaints [the perf. issues weren't all strictly related to double the data size, but also because the compiler handled singles better]. In this case the users wanted the speed rather than the exact sales commissions out to the penny every month, but of course the optimal solution would have been both, which a better compiler could have provided.

It takes a little extrapolation, but in the end, if the algorithms are correct, too often it can all boil down to either the compiler emitting good code, a programmer having to emit it via an assembler, or unhappy users.

In another project, the lead decided to do a general "usability" type user-group survey before and after "tuning" and there was a big difference that could only be explained by the improved performance. The users didn't really recognize that there was a perf. problem until they saw that it _could_ be faster, and when they saw the difference, the application became that much more quickly accepted. Users care about this stuff, and I've always been dismayed when it is thought of only at the end of a project.

Also, one of the advantages of the new design is that most of it doesn't _have_ to be used... If you don't need to make an argument 'invariant' just for performance reasons, you don't have to use it. Part of the problem with const littering code in C++ I think is because programmers often use it to be "const correct" when otherwise it might not make a lot of sense.

The more a language and compiler can do to make performance optimal when it's needed, and offset having to hack together shortcuts, workarounds or call into another language API, the better IMHO.
</soapbox>

> --bb
June 24, 2007
Bill Baxter wrote:
> Sean Kelly wrote:
>> Bill Baxter wrote:
>>> I too am disappointed that the only rebuttal from Walter on const by default has been "some C++ guys I talked to thought it was weird".
>>
>> Same here.  It's an elegant concept and I'd really like to give it a try.  I think only experience could tell whether it's a practical solution.
> 
> Oh I forgot -- he did give two reasons, actually:
> 1) some c++ guys he talked to think it's weird.
> 2) didn't want to have to have a mutable keyword.
>    (And in some other message later he said something about not wanting
> 'mutable' in D because it left a bad taste in the mouth of some C++ guys.)
> 

I thought we had established that "some C++ guys" will always find something wrong with any non-C++ language? That's not a good reason for anything.

-- 
Remove ".doesnotlike.spam" from the mail address.
June 24, 2007
Deewiant wrote:

> Bill Baxter wrote:
>> Sean Kelly wrote:
>>> Bill Baxter wrote:
>>>> I too am disappointed that the only rebuttal from Walter on const by default has been "some C++ guys I talked to thought it was weird".
>>>
>>> Same here.  It's an elegant concept and I'd really like to give it a try.  I think only experience could tell whether it's a practical solution.
>> 
>> Oh I forgot -- he did give two reasons, actually:
>> 1) some c++ guys he talked to think it's weird.
>> 2) didn't want to have to have a mutable keyword.
>>    (And in some other message later he said something about not wanting
>> 'mutable' in D because it left a bad taste in the mouth of some C++
>> guys.)
>> 
> 
> I thought we had established that "some C++ guys" will always find something wrong with any non-C++ language? That's not a good reason for anything.
> 

Yep, it's probably the biggest non-reason there is in language design.

-- 
Lars Ivar Igesund
blog at http://larsivi.net
DSource, #d.tango & #D: larsivi
Dancing the Tango
June 24, 2007
"Christopher Wright" <dhasenan@gmail.com> wrote in message news:f5kjll$92s$1@digitalmars.com...
> Not at all. A final variable cannot be reassigned; the reference is const, but the data is mutable.

Thanks - this has been confusing the heck out of me.  The problems seem to all stem from (a) coming from other languages and (c) overloading of keywords. The *concepts* are straightforward, it's knowing which keyword or combination of keywords means what.

A picture speaks a thousand words, so we REALLY REALLY REALLY need a TABLE here.

I have started one below  (you need a fixed-pitch font to view it, courier, courier new or fixedsys). I'd be very grateful if somebody (pref. Walter, since he likely knows more than anybody about all this) would correct/complete the table and repost it here.

LOCAL/GLOBAL VARIABLE
+---------------------------+-----------------------------------------+-----------------------------------------+
| KEYWORD(S)                |               VALUE TYPE                |
REFERENCE TYPE             |
|
+-------+------+-------+------------------+-------+------+-------+------------------+
|                           | CAN   | CAN   | CAN   | INVARIANCE      | CAN
| CAN   | CAN   | INVARIANCE      |
|                           | MOD   | MOD   | MOD   | BEGINS          | MOD
| MOD   | MOD   | BEGINS          |
|                           | DECL  | DATA  | DATA  |                 | DECL
| DATA  | DATA  |                 |
|                           | REF   | VIA   | VIA   |                 | REF
| VIA   | VIA   |                 |
|                           |       | DECLR | OTHER |                 |
| DECLR | OTHER |                 |
|                           |       | REF   | REF   |                 |
| REF   | REF   |                 |
+---------------------------+-------+-------+-------+-----------------+-------+-------+-------+-----------------+
| <none>                    | NO(1) | YES   | YES   | N/A             | YES
| YES   | YES   | N/A             |
| invariant                 | NO(2) | NO    | NO(3) | AT COMPILE TIME | NO
| NO    | NO(3) | COMPILE TIME(5) |
| const                     | NO(2) | NO    | NO(3) | AT COMPILE TIME | NO
| NO    | NO(3) | COMPILE TIME(5) |
| const invariant (4)       | NO(2) | NO    | NO(3) | AT COMPILE TIME | NO
| NO    | NO(3) | COMPILE TIME(5) |
| final                     | NO(2) | NO    | YES   | AT COMPILE TIME | NO
| YES   | YES   | RUN TIME        |
| final const               | NO(2) | NO    | NO    | AT COMPILE TIME | NO
| NO    | YES   | RUN TIME        |
| final invariant           | NO(2) | NO    | NO    | AT COMPILE TIME | NO
| NO    | NO    | RUN TIME        |
| final const invariant (4) | NO(2) | NO    | NO    | AT COMPILE TIME | NO
| NO    | NO    | RUN TIME        |
+---------------------------+-------+-------+-------+-----------------+-------+-------+-------+-----------------+

FUNCTION ARGUMENT
+---------------------------+-----------------------------------------+-----------------------------------------+
| KEYWORD(S)                |               VALUE TYPE                |
REFERENCE TYPE             |
|
+-------+------+-------+------------------+-------+------+-------+------------------+
|                           | CAN   | CAN   | CAN   | INVARIANCE      | CAN
| CAN   | CAN   | INVARIANCE      |
|                           | MOD   | MOD   | MOD   | BEGINS          | MOD
| MOD   | MOD   | BEGINS          |
|                           | DECL  | DATA  | DATA  |                 | DECL
| DATA  | DATA  |                 |
|                           | REF   | VIA   | VIA   |                 | REF
| VIA   | VIA   |                 |
|                           |       | DECLR | OTHER |                 |
| DECLR | OTHER |                 |
|                           |       | REF   | REF   |                 |
| REF   | REF   |                 |
+---------------------------+-------+-------+-------+-----------------+-------+-------+-------+-----------------+
| <none>                    | NO(1) | YES(6)| NO    | FUNC PROLOGUE   |
|       |       |                 |
| invariant                 | NO(1) | NO    | NO(3) | FUNC PROLOGUE   |
|       |       |                 |
| const                     | NO(1) | NO    | NO(3) | FUNC PROLOGUE   |
|       |       |                 |
| const invariant (4)       | NO(1) | NO    | NO(3) | FUNC PROLOGUE   |
|       |       |                 |
| final                     | NO(1) | NO    | YES(7)| FUNC PROLOGUE   |
|       |       |                 |
| final const               | NO(1) | NO    | NO    | FUNC PROLOGUE   |
|       |       |                 |
| final invariant           | NO(1) | NO    | NO    | FUNC PROLOGUE   |
|       |       |                 |
| final const invariant (4) | NO(1) | NO    | NO    | FUNC PROLOGUE   |
|       |       |                 |
+---------------------------+-------+-------+-------+-----------------+-------+-------+-------+-----------------+

(1) - NOT VISIBLE TO PROGRAMMER; EXISTS IN SYMBOL-TABLE/STACK/LOAD-FROM-MEMORY-INSTRUCTIONS ONLY

(2) - NOT VISIBLE TO PROGRAMMER; EXISTS IN
SYMBOL-TABLE/LOAD-FROM-MEMORY-INSTRUCTIONS ONLY
      AND MAY EVEN HAVE BEEN REPLACED BY IMMEDIATE LOAD, DEPENDING ON
IMPLEMENTATION AND TYPE.

(3) - CREATING OTHER REFERENCES IS NOT ALLOWED

(4) - SHOULD THIS BE LEGAL FOR VALUE TYPES? CERTAINLY REDUNDANT FOR THEM...

(5) - IMPOSSIBLE FOR REFERENCE TYPES THAT NEED CONSTRUCTORS/DESTRUCTORS; USE "FINAL INVARIANT" INSTEAD

(6) - USE OF A BY-VALUE ARGUMENT AS A LOCAL VARIABLE (ARGUABLY A "BAD THING [TM]")

(7) - POSSIBLE WITHIN THE FUNCTION, BUT WHY WOULD YOU WANT TO, ITS ONLY A **COPY OF** THE ORIGINAL ARGUMENT!!


June 24, 2007
"Martin Howe" <martinhowe@myprivacy.ca> wrote in message news:f5leib$1rp4$1@digitalmars.com...
> ...

Damn line wrapping!!!

Ok, try it as an attachment...



June 24, 2007
On Sat, 23 Jun 2007 20:44:41 +0400, Walter Bright <newshound1@digitalmars.com> wrote:

> Reiner Pope wrote:
>> You mention functional programming a fair bit with respect to const, which is nice to hear. But nothing in the current const system allows you to declare a verifiably 'pure' function; can we expect some annotation for functions which says 'this function doesn't read/write any global variables?'
>
> You're right, we're moving in the right direction, but we're not there yet.

Here is an interesting quote from Joe Armstrong dissertation "Making reliable distributed systems in the presence of software errors" [1] (I highlight most important fragments by **):

<quote>
In the above table the code has been subject to a simple analysis which
superficially classifies each module or function as “clean” or “dirty.” A
module is considered dirty if any function in the module is dirty, otherwise
it is clean. To simplify matters I say that a function is dirty if it sends or
receives a message or if it calls one of the following Erlang BIFs:
...
The reason for this classification is that any code fragment which calls
one of these BIFs is potentially dangerous.

**Notice that I have chosen a particularly simple definition of “dirty.”** At
first sight it might appear that it would be better to recursively define a
module as being dirty if any function in the module calls a “dangerous”
BIF or a dirty function in another module. Unfortunately with such a
definition virtually every module in the system would be classified as dirty.

The reason for this is that if you compute the transitive closure of all
functions calls exported from a particular module, **the transitive closure
will include virtually every module in the system**. The reason why the
transitive closure is so large is due to “leakage” which occurs from many
of the modules in the Erlang libraries.

**We take the simplifying view** that all modules are well-written and
tested, and that if they do contain side-effects, that the module has been
written in such a way so that the side effects do not leak out from the
module to adversely affect code which calls the module.
</quote>
$8.3, p.171.

Armstrong said that about the AX301 software -- more the 1.1 millions of Erlang code (this is one of the biggest FP software in the world). But even in pure functional Erlang there isn't special marks for 'pure/clean' or 'impure/dirty' functions. Because it can lead to that almost all modules in a system would be 'dirty'.

Adding 'pure' modificator to D can lead, IMHO, to the following alternatives:
1) nobody would use 'pure' at top level of modules/libraries/frameworks, because any top-level function/method would use some 'impure' function inside, or
2) it is necessary to introduce some constructs into language to define 'logical pureness' -- function will be marked as pure, but can use impure calls inside. Like 'mutable' keyword in C++ used for 'logical constantness'.

[1] http://www.sics.se/~joe/thesis/armstrong_thesis_2003.pdf

-- 
Regards,
Yauheni Akhotnikau
June 24, 2007
On Sun, 24 Jun 2007 07:44:33 +0300, Bill Baxter <dnewsgroup@billbaxter.com> wrote:
> Don Clugston wrote:
>> Walter Bright wrote:
>
>>> You can't do that with C/C++ because arrays can be aliased. I have a stack of papers 6" deep in the basement on trying to make C more suitable for numerics, and it's mostly about, you guessed it, fixing the alias problem. They failed.
>>  How much of this will actually be solved by invariant?
>> It seems to me that typically, arrays are built up element-by-element, even if they never change again. I guess in D2.0, such code would initialize a temporary array, and finally idup it?
>>  The aliasing problem will be far from gone, though. Consider an in-place operation on a matrix. Frequently you modify one only row at a time, based on the remaining rows. I think you'd have to cast(invariant) those other rows, to avoid aliasing.
>
> Thanks for asking this.  It's been eating at me too.  From what I understand of Fortran code, you declare an array of a given size, set its values however you like, then pass it to a function.  But if we make an invariant D array we can't set its values after initialization.  So it doesn't seem very useful.  Or is the use pattern going to be, as you say, Don, casting to invariant every time you need to call fast_math_array_function()?  If that's the case I suspect it won't take long before this becomes an idiom:
>
>     /* x += y array-style */
>     add_accum(double[] x, const double[] y) {
>        _add_accum_impl(cast(invariant)x,cast(invariant)y);
>     }
>
> Is that really what we want?
> That code can't even be right though, because x is *supposed* to get modified there. So how do you denote lack of aliasing on something like that using invariant?  In this respect C99's 'restrict' seems to be a lot more understandable.  My naive understanding of it being that you just slap it on any argument that you want to tell the compiler it can assume to be non-aliased.
>
> --bb

I've been asking the same questions myself too.

In serial coding, will 'invariant' only be used (in practice) to solve the aliasing problem?
You have a mutable data structure, and you're passing it as an invariant to functions.
Lots of 'cast(invariant)' will occur.

In parallel coding, 'invariant' will be more useful. However, truly 'invariant' data cannot ever
be changed. What I would want is a way to tell the compiler that the data will not be changed
*during the execution* of a function.


Hm, let there be the following function:

  void foo(invariant Bar bar);

Then I could use mutexes and casting as follows:

  MUTEX_LOCK(a)
  foo(cast(invariant)a);
  MUTEX_UNLOCK(a)

So 'cast(invariant)' will occur again.
The *programmer* needs to be sure that data passed to functions is truly invariant.


What if we drop out the 'invariant' keyword, and simply define that aliasing
is illegal (by default) and result in undefined behaviour?
That is, 'const' would mean a read-only view to immutable data (that cannot be changed
during the execution of a function).
June 24, 2007
On Sat, 23 Jun 2007 17:22:41 +0300, Reiner Pope <some@address.com> wrote:
> I think that invariant-as-storage-class vs. const-as-storage-class is a false dichotomy, and also that they are incorrectly associated with the completely different type-constructors, const() and invariant().
>
>
> As storage classes, const and invariant both mean "compile-time constant" -- their initializers must be evaluatable at compile time, and their data need not be stored in memory.
>
> However, apparently, data referred to by const-as-storage-class could actually be liable to change (this is cited as the difference between const-as-storage-class and invariant-as-storage-class).
>
> Yet this seems to clash with the idea of being initialized at compile time: in order for the data pointed to by const-as-storage-class to be changed by another reference, there must be a mutable pointer from which such a variable is initialized. Something like:
>
> void main() {
>     int a = 10;
>     int* p = &a; // here's our mutable view
>     const int* cp = &a; // here's our const-as-storage-class
> }
>
> At the moment, this doesn't compile, because &a is not evaluatable at compile time (even though the address of a is statically known). Assuming this is never supported (and I don't think that it should be), then the data pointed to by a const-storage-class variable can never be changed, so the data is really invariant. In that case, const-as-storage-class should be abolished, as it is identical to invariant-as-storage-class.[1]
>
> ---
>
> I also think that the idea of a "compile time constant" is distinct from invariant-as-type-constructor. For the purposes of clarity, I think it would ideally be better to make this distinction clear, and give this storage class a different name (sorry about Yet Another Keyword). I think 'define' could make sense:
>
>      define PI = 3.14;
>      define LanguageName = "D Programming Language";
>
> This also emphasises the similarity in meaning to #define.
>
> Of course, typeof(LanguageName) == invariant(char[])
>
> ---
>
> [1] Although the data pointed to by const-as-storage-class *is* invariant, the type system doesn't believe it. The following doesn't compile (correctly so, according to the specs)
>
>      const char[] a = "abc";
>      invariant char[] b = a;
>
> but how can the data in 'a' possibly change?
>
>
>
>     Reiner


I also thought about using a different keyword for compile-time constants.
I used 'literal' instead of 'define' though. :)

  const int a = 10;  //ok, compile-time constant

  const int b = f();  //error (assuming 'f()' cannot be evaluted at compile time)

I think that can be a bit confusing. I would like that 'b' would be immutable,
not a compile-time constant. (Yep, I know, 'final' is for that, but still...)


After that change, 'final' would be useless for value types.
Unfortunately, refence types (and pointers) are more problematic.

Old: const int v = 10;
New: literal int v = 10;

Old: const int v = f();
New: const int v = f();

Old: final const(MyClass) v;
New: const MyClass v;

Old: const(MyClass) v;
New: const(MyClass) v;

Old: final MyClass v;
New: ?
June 24, 2007
Sean Kelly wrote:
> Bruno Medeiros wrote:
>> Sean Kelly wrote:
>>>
>>>>> I can appreciate that 'invariant' may be of tremendous use to the compiler, but I balk at the notion of adding language features that seem largely intended as compiler "hints."
>>>>
>>>> It's a lot more than that. First, there's the self-documenting aspect of it. Second, it opens the way for functional programming, which can be of huge importance.
>>>
>>> Could you explain?  I've been trying to determine how I would write a library using these new features, and so far, 'invariant' simply seems more an obstacle to maintainability (because of the necessary code duplication), than it is an aid to programming.
>>>
>>
>> In which situation does having 'invariant' lead to code duplication? I can see the common situation where you have class getter methods, or other similiar methods, but even without 'invariant' and only with const and non-const you have that problem.
> 
> Let's say I am writing a linear search routine for arrays.  The most obvious implementation would accept that array as 'const', but it may be more optimal to use 'invariant'.  Since not all searchable arrays will be invariant however, both implementations must exist.  That said, a template function could likely handle both situations with the same code, and hopefully it will be uncommon to want virtual class routines that take both types of parameters.  Though I guess the same could be done there with a single template implementation and aliases.
> 

Well, but that code duplication (even if simplified by the use of templates), is optional, because the invariant version of that function is just an optimization that can be made for some kinds of arrays. With only const in the language, but no invariant, you don't even have that option, so you are not any worse by having invariant in the language (in terms of the aformentioned code duplication).

I think it has now been clearly established the usefulness of having both const and invariant type modifiers (which Walter has explained about a lot), and I hope everyone understands that now.
We should focus now on the implementation details of our current proposal, for which there are several issues:

* Having invariant/const being used differently as a storage-class and type-modifier. Is this a good thing? (IMO, no, it is a major unnecessary source of confusion)

* The fact that the top-level type is never declared const/invariant when using const/invariant as type modifiers. Is this a horrible breach in consistency, or is actually a key necessary aspect of the design? (IMO, it's either one or the other, but I'm still not sure which)

* How to deal with code duplication, especially in class getter methods. where the only thing that changes is the type modifier of the implicit 'this' parameter and some other input or output.

* How will meta-programming, specialization, IFTI, etc. work with these type modifiers?

* ... and other issues that there may be.

-- 
Bruno Medeiros - MSc in CS/E student
http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
June 24, 2007
Bill Baxter wrote:
> Don Clugston wrote:
>> Walter Bright wrote:
> 
>>> You can't do that with C/C++ because arrays can be aliased. I have a stack of papers 6" deep in the basement on trying to make C more suitable for numerics, and it's mostly about, you guessed it, fixing the alias problem. They failed.
>>
>> How much of this will actually be solved by invariant?
>> It seems to me that typically, arrays are built up element-by-element, even if they never change again. I guess in D2.0, such code would initialize a temporary array, and finally idup it?
>>
>> The aliasing problem will be far from gone, though. Consider an in-place operation on a matrix. Frequently you modify one only row at a time, based on the remaining rows. I think you'd have to cast(invariant) those other rows, to avoid aliasing.
> 
> Thanks for asking this.  It's been eating at me too.  From what I understand of Fortran code, you declare an array of a given size, set its values however you like, then pass it to a function.  But if we make an invariant D array we can't set its values after initialization.  So it doesn't seem very useful.  Or is the use pattern going to be, as you say, Don, casting to invariant every time you need to call fast_math_array_function()?  If that's the case I suspect it won't take long before this becomes an idiom:
> 
>    /* x += y array-style */
>    add_accum(double[] x, const double[] y) {
>       _add_accum_impl(cast(invariant)x,cast(invariant)y);
>    }
> 
> Is that really what we want?
> That code can't even be right though, because x is *supposed* to get modified there. So how do you denote lack of aliasing on something like that using invariant?  In this respect C99's 'restrict' seems to be a lot more understandable.  My naive understanding of it being that you just slap it on any argument that you want to tell the compiler it can assume to be non-aliased.
> 
> --bb
Functional languages don't entirely solve the aliasing problem, because keeping everything immutable means arrays would need to be copied all over the place. Clean has uniqueness types, which allow the type system to specify that your pointer is the only one to that data. I don't know how they work, but maybe they could be an appropriate solution? (Not to mention that they've been proposed before to avoid the cast-to-invariant after creating new data)

Much of the enforcement work, I think, could be done by scope (because it tells you that functions you call won't keep a pointer to your data). After that, it might give you 'restrict', but actually type-checked, and basically for free.

(But that's only a guess)

   Reiner