August 24, 2003
In article <bi9df5$1bh7$1@digitaldaemon.com>, Philippe Mori wrote:
> 
>> > So your definition of immuable that an object won't changes... but for a function parameter, does it apply to both the source and target or only to the target (as const in C++).
>>
>> What do you mean by source and target in this context?
>> Immutable's just an object that will never change after it has been
>> initialized.
>>
> 
> Suppose something similar to this:
> 
> void f(int &target) { ... }
> int source = 1;
> f(source);
> 
> The source is from where the data come... Can source be modified externally (other thread, alias, while f is executing.

If it's immutable, then it cannot. If it's mutable, like in your example, it can. This also means that there is an fundamental incompatibility between mutable and immutable objects: one cannot be converted to the other, at least not without violating the basic rules of the language.

(References changed to inout to look more like D. After all, this is a D
newsgroup...)

void f(inout immutable int);
int source = 1;
f(source); // compile-time error: can't convert int to immutable int

If you could do the conversion, you might be able to change the object in another thread or via aliasing, and it would change without f() knowing. Which would make immutable the same as C++'s const, and prevent the precious compiler optimizations that we're trying to achieve here (or, are we? I actually doubt that they'd be so significant)

>> > prohibited would be a function modifier that will ensure that the function is never called.
>>
>> Then why put the function there in the first place? :)
>>
> 
> I see a few cases:
> 
> - The functions is implictly generated

I guess there aren't implicitly generated functions in D currently (I
might be wrong?)

> - There are some implicit conversion that are allowed but does not give the desired result (for ex. in C++ std::string class can be initialized from 0.0 or false because of implicit conversion that occurs but are not desirable in that specific context). In practice, if we find such a bug, then we have an easy way to ensure we won't have it another time.

I think D doesn't have as many opportunities to shoot oneself in the foot as C++, particularly with respect to implicit conversions... Then again, maybe there is someone to know better.

> - One existing function become obsolete and we want to ensure that it is not used anymore but because there are other similar overload we like the compiler to trap them and issue an error. A keyword allows to make it clearer...  Otherwise it is possible to do it with templates but it is more complicated...

D has deprecated: http://www.digitalmars.com/d/attribute.html

> - In generic (template) code, we might know some undesired types for arguments (like swapping one constant object or swapping objects of distincts type). If we have a prohibited keyword, it is a lot easier to prohibit what we don't want that to make code that would causes a compilation error in those cases...

You might be wanting to abuse the deprecated (or private) qualifiers to
do that kind of prohibiting. All of those keywords would play partly the
same role, which isn't exactly orthogonal language design. I'd rather go
in the direction of general compile-time error message which can have
a lot more uses than just prohibiting instantiation of a certain
function.

class X { }

template (Foo) {
    // your general template code goes here
}
template (Foo : X) {
    compileTimeError("You shouldn't instantiate Foo with types derived "
                     "from X");
}

Upon finding an "instance Foo(X)" the compiler would blurt out something like

line 567: Cannot instantiate Foo(X)
line 123: compileTimeError: You shouldn't instantiate Foo with types
derived from X

> I have done it (preventing code from compiling) a lot of times in
> C++ when I was refactoring the code that have known problems.
> I was using the compiler to help me find those problematic places...

If you can find an example that isn't solved by other features in D, people are likely to be interested in seeing it.

-Antti

August 24, 2003
Hi,

    Comments embedded.

----- Original Message -----
From: "Antti Sykäri" <jsykari@gamma.hut.fi>
Newsgroups: D
Sent: Tuesday, August 19, 2003 8:14 PM
Subject: const / immutable (was: C++ style const concept)


[big snip]

> Now that I think of it, I remember that Daniel Yokomiso wrote an interesting description of const stuff some time ago, let's see... There, I see it's still worth reviewing: http://www.digitalmars.com/drn-bin/wwwnews?D/9962

    Unfortunately my memory is not well lately and the newsgroup web
interface seems to be down, so I talk something about this mutability
issues. It's related to my pet language Eon.

> (Daniel: how's Eon doing, by the way?)

    Eon is dead, long live Eon. Or something like that. The worst thing
about designing a language is the pesky design decisions you have to make.
I, after one year of study, dropped side-effects, and now Eon is a pure
OOFPL. Here is the story of my attempts to provide immutability in the
type-system. First some definitions:

1 - by side-effects I mean both observable and unobservable side-effects.
2 - I'll use procedures to describe methods with side-effects and functions
to describe "pure", side-effect free, methods.
3 - mutable types are subtypes of immutable types, so we can call functions
and procedures on mutable types and only functions on immutable types.
4 - a variable with an immutable type A holds a reference to some value of
A, but this value can be mutated through other (mutable) references.

    My desire was to provide a way to verify, via static type checking, code
correctness, specially regarding DbC. One of the most powerful contracts is
the immutability contract, guaranteeing that some parameters of the
operation will be untouched.


int sum(immutable int[] array) {
    int result = 0;
    for (int i = 0; i < array.length; i++) {
        result += array[i];
    }
    return result;
}


    The "immutable" modifier indicates that the parameter won't be changed
by the operation body. Notice that a immutable array of type T is distinct
from a array of type immutable T; this "immutable" modifier refers to the
array immutability, not the array element type immutability. This type
modifier should be carried through assignment to avoid cheating, or else the
operation could store the value in a mutable variable and break the
immutability. There's a second way to use the "immutable" modifier: to
define pure functions.


int immutable product(immutable int[] array) {
    int result = 0;
    for (int i = 0; i < array.length; i++) {
        result *= array[i];
    }
    return result;
}


    An "immutable" operation won't cause side effects, so inside a class you
can separate functions from procedures to ensure immutability. The compiler
could enforce this rule disallowing procedure calls through immutable
references. As soon as we define such rules we get in some problems:


template TMin(T) {
    T immutable min(immutable T x, immutable T y) {
        return x < y ? x : y
    }
}


    The min operation correctly asserts that it's parameters aren't mutated,
but it incorrectly transform the immutable reference into a mutable one,
using the "return". Here comes the first problem of immutability
definitions: some operations return types depend on its parameter types. In
these situations the return type mutability status is defined by the caller.
    Immutability depends heavily on the compiler, both to check the calls
and to infer correctly the return type dependencies.
    There's also a third type of immutability: "immutable" types. But
they're a beast of their own.
    In my language I decided to drop side-effects because: it's very
expensive to track immutability and to provide two versions of the same
operations (e.g. a put method that changes the collection and a put method
that returns a new colection with the new value added). Also it'd very hard
to write "ensure" clauses on method's contracts that have side-effects
(google for "old" expression and clone in comp.lang.eiffel). Geting rid of
side-effects allow the language and the libraries to become simpler, and we
can simulate side-effects with linear types and monads.

    So, answering your question Antti, Eon is being rewritten by the last
time. Today it's a pure OOFPL, with a higher-order features, a simple syntax
to define object literals (i.e. "pair := {first := 1; second := "abcde"}"),
predicate based multiple-inheritance (i.e. "class a := {x : Integer; y :
Real and (> x) and odd};", we define x as an Integer and y as a Real number,
greater than x and odd (odd and (> x) are predicates)). Unfortunately I had
to restart the compiler (again) due to the syntatic and semantic changes. I
believe this will be the last big change in the language and sometime in the
next year (first semester probably) I'll have a working compiler and a
standard library (collections, text, numerics, xml, io and networking) -
most parts of the library are already specified (I have the method types and
contracts and some implementations written in older versions of Eon).

> -Antti

    Best regards,
    Daniel Yokomiso.

"Nature has given us two ears but only one mouth."
 - Benjamin Disraeli (1804-81), British politician


---
Outgoing mail is certified Virus Free.
Checked by AVG anti-virus system (http://www.grisoft.com).
Version: 6.0.512 / Virus Database: 309 - Release Date: 19/8/2003


August 25, 2003
>
> If it's immutable, then it cannot. If it's mutable, like in your example, it can. This also means that there is an fundamental incompatibility between mutable and immutable objects: one cannot be converted to the other, at least not without violating the basic rules of the language.
>
> (References changed to inout to look more like D. After all, this is a D
> newsgroup...)
>
> void f(inout immutable int);
> int source = 1;
> f(source); // compile-time error: can't convert int to immutable int
>

If I want to uses an in parameter, does I have the choice between by value which I think is the default and by reference...

So it would be more like

void f(in byref immutable int)    // More D style syntax

or

void f (in immutable & int the_param)    // More like C++

To tell the compiler that I do not want to modify the_param and that it can assumes that it won't be modified externally.

> If you could do the conversion, you might be able to change the object in another thread or via aliasing, and it would change without f() knowing. Which would make immutable the same as C++'s const, and prevent the precious compiler optimizations that we're trying to achieve here (or, are we? I actually doubt that they'd be so significant)
>


>
> I guess there aren't implicitly generated functions in D currently (I
> might be wrong?)
>
> > - There are some implicit conversion that are allowed but does not give the desired result (for ex. in C++ std::string class can be initialized from 0.0 or false because of implicit conversion that occurs but are not desirable in that specific context). In practice, if we find such a bug, then we have an easy way to ensure we won't have it another time.
>
> I think D doesn't have as many opportunities to shoot oneself in the foot as C++, particularly with respect to implicit conversions... Then again, maybe there is someone to know better.
>
> > - One existing function become obsolete and we want to ensure that it is not used anymore but because there are other similar overload we like the compiler to trap them and issue an error. A keyword allows to make it clearer...  Otherwise it is possible to do it with templates but it is more complicated...
>
> D has deprecated: http://www.digitalmars.com/d/attribute.html
>

But does it is possible to control in which module or on some other condition when deprecated take effects. Maybe I am using 2 libraries that has some deprecated functions but I still want to uses them in the first one (the code depends a lot on them) but not on the second one...

Also if the attribute can be depedant on other things (probably on versioning), then it might be possible to uses version for that purpose...

> > - In generic (template) code, we might know some undesired types for arguments (like swapping one constant object or swapping objects of distincts type). If we have a prohibited keyword, it is a lot easier to prohibit what we don't want that to make code that would causes a compilation error in those cases...
>
> You might be wanting to abuse the deprecated (or private) qualifiers to
> do that kind of prohibiting. All of those keywords would play partly the
> same role, which isn't exactly orthogonal language design. I'd rather go
> in the direction of general compile-time error message which can have
> a lot more uses than just prohibiting instantiation of a certain
> function.
>
> class X { }
>
> template (Foo) {
>     // your general template code goes here
> }
> template (Foo : X) {
>     compileTimeError("You shouldn't instantiate Foo with types derived "
>                      "from X");
> }
>
> Upon finding an "instance Foo(X)" the compiler would blurt out something
like
>
> line 567: Cannot instantiate Foo(X)
> line 123: compileTimeError: You shouldn't instantiate Foo with types
> derived from X
>

Compile time error is a must... This allows to check many constraints that are not directly supported by the language (like your example).

This can be used to validate some constants to ensure no accidental
changes would be done. This include some magic numbers, the size
of a structure (it's alignment, member offset,...) and we should be able
also to check some properties of classes and struct like:

- Is it a POD type, auto type, class type, enum type,...
- Is it modifiable
- Does it have a given member or support a given operation
(member function, copy constructor, default constructor, operator +,
comparison, equality,...)
- Is if final, abstract,...
- Is it an interface (or a COM compatible interface)

> > I have done it (preventing code from compiling) a lot of times in
> > C++ when I was refactoring the code that have known problems.
> > I was using the compiler to help me find those problematic places...
>
> If you can find an example that isn't solved by other features in D, people are likely to be interested in seeing it.
>

I haven't yet tried the D compiler but I think that it might be interesting to check what can be done in D form the following source (and others):

- Modern C++ Design (book) & Loki
- C++ Templates (book)
- STL
- boost
- ATL
- C#, .NET and managed C++
- Xena
...

We would then be able to compare the power and the simplicity of the language for some concrete things... and if something cannot be done directly, how hard it is to be done in D (if he can be done) or is it necessary (or we have better alternatives).

It would also be interesting to check from some of well known book
like Effective C++, Effective STL, and others that give some rules
how to uses C++ more safely, how D compare (avoid problem
at least implicitly while retaining enough power to be usefull...)


August 25, 2003
In article <bid8ts$12qg$1@digitaldaemon.com>, Philippe Mori wrote:
>> (References changed to inout to look more like D. After all, this is a D
>> newsgroup...)
>>
>> void f(inout immutable int);
>> int source = 1;
>> f(source); // compile-time error: can't convert int to immutable int
>>
> 
> If I want to uses an in parameter, does I have the choice between by value which I think is the default and by reference...
> 
> So it would be more like
> 
> void f(in byref immutable int)    // More D style syntax

Whoops :) Of course immutable objects would be "in" since they cannot be changed. So I'd say:

void f(in immutable int);

or

void f(in immutable Big_Object);

Were this legal D, the compiler would be free to pick either by value or by reference (whichever it would think to be more efficient) -- the semantics wouldn't change.

If it were const, the semantics _would_ change; consider

int f(inout const int x, inout y)
{
    y++;
    return x;
}

void main()
{
    int a = 1;
    printf("%d\n", f(a, a));
}

If f gets y and x by reference, main() will print 2, and if by value, it will print 1.  This is one of the places where const can deceive.

D doesn't specify by-reference or by-value, instead it relies on in/out/inout, as mentioned in the "Functions" section.

>> D has deprecated: http://www.digitalmars.com/d/attribute.html
> 
> But does it is possible to control in which module or on some other condition when deprecated take effects. Maybe I am using 2 libraries that has some deprecated functions but I still want to uses them in the first one (the code depends a lot on them) but not on the second one...

Sounds like a quality of implementation issue; DMD has option:

  -d             allow deprecated features

I'd say that in this case, the answer is "yes" if you build the two projects separately.

-Antti

August 26, 2003
In article <biafl2$2tsa$1@digitaldaemon.com>, Daniel Yokomiso wrote:
>     Eon is dead, long live Eon. Or something like that. The worst thing
> about designing a language is the pesky design decisions you have to make.
> I, after one year of study, dropped side-effects, and now Eon is a pure
> OOFPL. Here is the story of my attempts to provide immutability in the
> type-system. First some definitions:
> 
> 1 - by side-effects I mean both observable and unobservable side-effects.
> 2 - I'll use procedures to describe methods with side-effects and functions
> to describe "pure", side-effect free, methods.
> 3 - mutable types are subtypes of immutable types, so we can call functions
> and procedures on mutable types and only functions on immutable types.
> 4 - a variable with an immutable type A holds a reference to some value of
> A, but this value can be mutated through other (mutable) references.

So this would be equivalent to C++'s const. (Without the possibility of subverting the type system, though.)

I'll blabber a bit more about immutability below.

> int sum(immutable int[] array) {
>     int result = 0;
>     for (int i = 0; i < array.length; i++) {
>         result += array[i];
>     }
>     return result;
> }
> 
> 
>     The "immutable" modifier indicates that the parameter won't be changed
> by the operation body. Notice that a immutable array of type T is distinct
> from a array of type immutable T; this "immutable" modifier refers to the
> array immutability, not the array element type immutability. This type

One interesting question regarding the semantics of immutable here is that what is the difference between immutable array of int and array of immutable int. Because the array can be viewed as consisting of its elements, doesn't its immutability imply the immutability of its elements and therefore make it equivalent to immutable array of immutable integers?

It seems to depend on the kind of array and of the kind of the language it lives in.

I'd assume (in pseudo-code notation) that array in a very "pure" modern, orthogonal, clean, type-safe etc. language (I'm putting some expectations on Eon you see after all those papers you've read :-) would have operations like:

class immutable_array<T>
{
    T get(int); // return by-value copy of element at i
    int length();
}

class array<T> extends immutable_array<T>
{
    void set(T, int) // set value at T to int
    void length(int);
}

This would mean in practice that having an immutable array would prohibit changing its elements, which would render them practically immutable.

On the other hand, if the hypothetical language were less clean and had something as down-and-dirty as aliasing and references, what would an array then look like? Like this?:

array<T>
{
    T& get(int); // return a reference to element at i
    // other properties
}

Now what would the "immutable part of the interface" look like? Would it have the function "immutable T& get(int)"? But this would have the same signature, except for the hidden argument that contains the reference to the object the method is part of. Ah, there's the catch:

immutable_array<T>
{
    immutable T& immutable get(int);
    // other properties
}

And lo, we get C++ rules along with all the special cases and all. (There probably is some kind of logic to them after all, especially if you remove the warts induced by the C compatibility.)

If you have by-reference object, say of type T, what does "immutable T" mean?  That you cannot change the reference to point to another object? Or that you can call only the pure methods of the object? Are the rules different if you have value objects?

> modifier should be carried through assignment to avoid cheating, or else the operation could store the value in a mutable variable and break the immutability. There's a second way to use the "immutable" modifier: to define pure functions.

At this phase I must point out that the semantics seem awfully familiar from C++, only the names differ:

method - member function
immutable - const
pure function - const member function

>     An "immutable" operation won't cause side effects, so inside a class you
> can separate functions from procedures to ensure immutability. The compiler
> could enforce this rule disallowing procedure calls through immutable
> references. As soon as we define such rules we get in some problems:
> 
> 
> template TMin(T) {
>     T immutable min(immutable T x, immutable T y) {
>         return x < y ? x : y
>     }
> }

Why not:

template TMin(T) {
    immutable T immutable min(immutable T x, immutable T y) {
        return x < y ? x : y;
    }
}

I'd assume it would be instantiated automatically. It would of course need along the normal version:

template TMin(T) {
    T min(T x, T y) { return x < y ? x : y; }
}

And the overloading rules to pick the right version.

>     In my language I decided to drop side-effects because: it's very
> expensive to track immutability and to provide two versions of the same
> operations (e.g. a put method that changes the collection and a put method
> that returns a new colection with the new value added). Also it'd very hard

I'd assume that you wouldn't need two versions; if you only want to have an immutable and a mutable version of a class, just drop the procedures and leave the pure methods in the immutable version -- calling the procedure on the immutable version would be prohibited at compile time, nice and clean.

Maybe you wanted something completely different?

> Also it'd very hard to write "ensure" clauses on method's contracts that have side-effects (google for "old" expression and clone in comp.lang.eiffel).

Shouldn't "ensure" clauses *not* have side-effects? Or did you mean "ensure" clauses of methods that have side-effects?

Uh, could you give an example of one hard case?

The thread you pointed at is probably the one with the subject "'old' + 'implies' = bug ?" (from 1996)? An interesting thread.  "old" is a funny modifier, but one wonders if the semantical trouble arised because its real semantics ("will be evaluated before entering the function although it is mentioned and used only at the end") was swept under the rug while humming noble tunes about mathematical theories about contracts.

And of course all the expressions in contracts are side-effectsless, at least if we don't cheat, right? *grin*

IMHO all things that go with contracts tend to be a bit flaky. I just read (OOSC 2nd ed.) about abstract contracts that make it possible to actually tighten the preconditions in a deriving class, which shouldn't be possible -- but in this case is.  (See the example about BOUNDED_STACK in the chapter about inheritance techniques.)

I've not finished studying Eiffel's contract system in full but I do have the feeling that it's designed with a bit too much theory in mind -- I'd rather concentrate in making the constructs as usable and transparent as possible than create overly complicated (or overly simplified) theories and then watch them break in normal practical situations.

>     So, answering your question Antti, Eon is being rewritten by the last
> time. Today it's a pure OOFPL, with a higher-order features, a simple syntax
[snip]
> standard library (collections, text, numerics, xml, io and networking) - most parts of the library are already specified (I have the method types and contracts and some implementations written in older versions of Eon).

Best of luck with Eon, the type system pretty looks interesting. Must be worth the intellectual exercise, at least. (I must confess that I don't put much value on purely functional languages, but that could be just because I haven't studied them enough. Perhaps I shall be enlightened one day and write only Haskell after that.)

To conclude, it seems that the efforts to combine mutable and immutable values tend to divert in different directions. Some (D) make the language simpler by forgetting constant types (almost) altogether. Some (Eon) go to the other extreme and make everything purely functional. Some (C++) march bravely forth and allow the complexity caused by having both the constant and the mutable. This makes wonder if there is at all a simple and beautiful language that would at the same time allow the constant and non-constant types to live peacefully together...

-A

August 26, 2003
Hi,

    Comments embedded.

----- Original Message -----
From: "Antti Sykäri" <jsykari@gamma.hut.fi>
Newsgroups: D
Sent: Monday, August 25, 2003 9:07 PM
Subject: Re: const / immutable (was: C++ style const concept)

[snip]

> So this would be equivalent to C++'s const. (Without the possibility of subverting the type system, though.)
>
> I'll blabber a bit more about immutability below.
>
> > int sum(immutable int[] array) {
> >     int result = 0;
> >     for (int i = 0; i < array.length; i++) {
> >         result += array[i];
> >     }
> >     return result;
> > }
> >
> >
> >     The "immutable" modifier indicates that the parameter won't be
changed
> > by the operation body. Notice that a immutable array of type T is
distinct
> > from a array of type immutable T; this "immutable" modifier refers to
the
> > array immutability, not the array element type immutability. This type
>
> One interesting question regarding the semantics of immutable here is that what is the difference between immutable array of int and array of immutable int. Because the array can be viewed as consisting of its elements, doesn't its immutability imply the immutability of its elements and therefore make it equivalent to immutable array of immutable integers?

    If you have (using const to mark immutability):


class Cell {
    private int value;
    this(int value) {
        this.value = value;
    }
    public void set(int value) {
        this.value = value;
    }
    public int immutable get() {
        return this.value;
    }
}

Cell c0 = new Cell(0), c1 = new Cell(1), c2 = new Cell(2), c3 = new Cell(3);
Cell[]                a0 = [c1, c2, c3]; // mutable array of mutable cell
references
immutable Cell[]      a1 = [c1, c2, c3]; // mutable array of immutable cell
references
Cell[immutable]       a2 = [c1, c2, c3]; // immutable array of mutable cell
references
const Cell[immutable] a3 = [c1, c2, c3]; // immutable array of immutable
cell references

a0[0].set(0); // ok, the a0[0] reference isn't immutable
a0[0] = c2;   // ok, the array isn't immutable
a1[0].set(0); // compiler error, the a1[0] reference is immutable
a1[0] = c2;   // ok, a1 isn't immutable
a2[0].set(0); // ok, the a2[0] reference isn't immutable
a2[0] = c2;   // compiler error, a2 is immutable
a3[0].set(0); // compiler error, the a1[0] reference is immutable
a3[0] = c2;   // compiler error, a3 array is immutable


    Therefore I think that separating the immutability issue of the array
vs. the element type leads to different semantics. Here we assume that
"immutable A" is a supertype of "A".


> It seems to depend on the kind of array and of the kind of the language it lives in.
>
> I'd assume (in pseudo-code notation) that array in a very "pure" modern, orthogonal, clean, type-safe etc. language (I'm putting some expectations on Eon you see after all those papers you've read :-) would have operations like:
>
> class immutable_array<T>
> {
>     T get(int); // return by-value copy of element at i
>     int length();
> }
>
> class array<T> extends immutable_array<T>
> {
>     void set(T, int) // set value at T to int
>     void length(int);
> }
>
> This would mean in practice that having an immutable array would prohibit changing its elements, which would render them practically immutable.


    Immutable operations ("T immutable get(int)" and "int immutable
length()") won't have side effects, but they may be changed by other
objects/threads. But if you have a value type (like a number, matrix, etc.),
a immutable struct type, you'll be safe, because the value isn't shared.
Immutability tell us nothing about aliasing.


> On the other hand, if the hypothetical language were less clean and had something as down-and-dirty as aliasing and references, what would an array then look like? Like this?:
>
> array<T>
> {
>     T& get(int); // return a reference to element at i
>     // other properties
> }
>
> Now what would the "immutable part of the interface" look like? Would it have the function "immutable T& get(int)"? But this would have the same signature, except for the hidden argument that contains the reference to the object the method is part of. Ah, there's the catch:
>
> immutable_array<T>
> {
>     immutable T& immutable get(int);
>     // other properties
> }
>
> And lo, we get C++ rules along with all the special cases and all. (There probably is some kind of logic to them after all, especially if you remove the warts induced by the C compatibility.)


    The problem in C++ is that they use "a[0] = 1;" meaning, get the address
of the first element and let it be assignable, so you need two versions of
array_get, one yielding const references and other yielding normal ones. IMO
"a[0]" should call array_get and "a[0] = 1" should call array_set, just
syntatic sugar.

> If you have by-reference object, say of type T, what does "immutable T" mean?  That you cannot change the reference to point to another object? Or that you can call only the pure methods of the object? Are the rules different if you have value objects?


array<T> {
    T const get(int);
    int const length();
    void set(int,T);
    void length(int);
}

int i1 = 1, i2 = 2, i3 = 3, i4 = 4;
array<int>              a1 = [i1, i2, i3, i4];
array<&int>             a2 = [i1, i2, i3, i4];
const array<int>        a3 = [i1, i2, i3, i4];
const array<&int>       a4 = [i1, i2, i3, i4];
const array<const &int> a5 = [i1, i2, i3, i4];

a1.set(0, i3);   // ok
a2.set(0, &i3);  // ok
a2.get(0) = 5;   // ok
a3.set(0, i3);   // compiler error
a4.set(0, &i3);  // compiler error, can't mutate the array.
a4.get(0) = 8;   // ok
a5.set(0, &i3);  // compiler error
a5.get(0) = 8;   // compiler error


   It's a bit tricky, but the const qualifies the reference type too.

> > modifier should be carried through assignment to avoid cheating, or else
the
> > operation could store the value in a mutable variable and break the immutability. There's a second way to use the "immutable" modifier: to define pure functions.
>
> At this phase I must point out that the semantics seem awfully familiar from C++, only the names differ:
>
> method - member function
> immutable - const
> pure function - const member function
>
>
> >     An "immutable" operation won't cause side effects, so inside a class
you
> > can separate functions from procedures to ensure immutability. The
compiler
> > could enforce this rule disallowing procedure calls through immutable references. As soon as we define such rules we get in some problems:
> >
> >
> > template TMin(T) {
> >     T immutable min(immutable T x, immutable T y) {
> >         return x < y ? x : y
> >     }
> > }
>
> Why not:
>
> template TMin(T) {
>     immutable T immutable min(immutable T x, immutable T y) {
>         return x < y ? x : y;
>     }
> }
>
> I'd assume it would be instantiated automatically. It would of course need along the normal version:
>
> template TMin(T) {
>     T min(T x, T y) { return x < y ? x : y; }
> }
>
> And the overloading rules to pick the right version.


    Hmmm, perhaps you've meant:


template TMin(T)

and


template TMin(immutable T)


    But the issue here is that immutability, when you're dealing with
generic types, generates some dependency between the parameters and the
result types. Sometimes only one parameter will define the const issue,
sometimes more than one. If you make a templated function to find
statistical results from a array (min, max, average, etc.) you need a way to
ensure that these functions won't: mutate the array, won't mutate the array
elements, the return types will have the same immutability status (yes or
no) that the array element type. In your example the TMin version for
mutable parameters allowed the min function to change them through any
mutating methods they had.


> >     In my language I decided to drop side-effects because: it's very
> > expensive to track immutability and to provide two versions of the same
> > operations (e.g. a put method that changes the collection and a put
method
> > that returns a new colection with the new value added). Also it'd very
hard
>
> I'd assume that you wouldn't need two versions; if you only want to have an immutable and a mutable version of a class, just drop the procedures and leave the pure methods in the immutable version -- calling the procedure on the immutable version would be prohibited at compile time, nice and clean.
>
> Maybe you wanted something completely different?


    I was talking about the issue I mentioned in my last (statistical)
example. You need to write things like:


template stats(T) {
    const_if_array_type_is_const T find_min(const T [const] array) { ... }
}


    That can be very awkward. The problem here is: the result type
immutability depends on the array element type immutability, but we must be
able to ensure that the array element type won't be changed by the function.
These examples may be simple, but when you have more classes, it's hard to
track immutability dependencies. Also immutability is one level only:


class Cell(T) {
    private T value;
    this(T value) {
        this.value = value;
    }
    public void set(T value) {
        this.value = value;
    }
    public T immutable get() {
        return this.value;
    }
}

Cell(int) c1 = new Cell(int)(1);
const Cell(Cell(int)) c2 = new Cell(Cell(int))(c1);
const Cell(const Cell(Cell(int))) c3 = new Cell(const Cell(Cell(int)))(c2);

c3.set(c2);            // compiler error;
c3.get().set(c1);      // compiler error;
c3.get().get().set(0); // ok;


    With "Cell" classes it's easy, because you can specify the internal
elements immutability status, but some classes, like "Department" that
contains "Employee", are harder, because you may mutate "Employee" through a
"immutable Department" reference, because you must be able to tell that the
"Employee get(String)" won't mutate the "Department", but when called via a
"immutable Deparment" reference should yield a "immutable Employee", but
when calles via a common "Department" reference should yield a common
"Employee". If we include the implicit this parameter in the type we may be
able to do this (somehow, but's awkward):


immutable Employee immutable get(immutable Department this, immutable String
name);
Employee immutable get(Department this, immutable String, name);


where the "immutable" qualifier before the "get" means the this reference
won't be mutated.
     Some issues regarding mutability are discussed here
<http://lambda.weblogs.com/discuss/msgReader$8378?mode=topic&y=2003&m=8&d=25
> Tim Sweeney has some pretty good thoughts.

> > Also it'd very hard to write "ensure" clauses on method's contracts that have side-effects (google for "old" expression and clone in comp.lang.eiffel).
>
> Shouldn't "ensure" clauses *not* have side-effects? Or did you mean "ensure" clauses of methods that have side-effects?
>
> Uh, could you give an example of one hard case?


    Sorry I meant: 'Also it's very hard to write "ensure" clauses on
contracts of methods that have side-effects (i.e. methods that mutate the
instance)'.

Set(T) {
    void remove(T item)
    out () {
        if (old this.contains(item)) {
            assert(this.size + 1 == (old this.size()));
        }
        assert(!this.contains(item));
// so far so good.
       for (T each; old this) {
           if (each != item) {
               assert(this.contains(item));
           }
       }
/* ops, it won't work because the "old this" expression will yield a
reference
 * to the this object, and here it's already mutated. The correct expression
is
 * "(old this.clone())" ensuring that we get a snapshot of the this object
from
 * before the method execution.
 */
    };
}


    This is the simplest case of "old" expressions in mutating methods.
There's more fun. Check the Eiffel kernel classes' contracts for more
details.


[snip]

> >     So, answering your question Antti, Eon is being rewritten by the
last
> > time. Today it's a pure OOFPL, with a higher-order features, a simple
syntax
> [snip]
> > standard library (collections, text, numerics, xml, io and networking) -
> > most parts of the library are already specified (I have the method types
and
> > contracts and some implementations written in older versions of Eon).
>
> Best of luck with Eon, the type system pretty looks interesting. Must be worth the intellectual exercise, at least. (I must confess that I don't put much value on purely functional languages, but that could be just because I haven't studied them enough. Perhaps I shall be enlightened one day and write only Haskell after that.)
>
> To conclude, it seems that the efforts to combine mutable and immutable values tend to divert in different directions. Some (D) make the language simpler by forgetting constant types (almost) altogether. Some (Eon) go to the other extreme and make everything purely functional. Some (C++) march bravely forth and allow the complexity caused by having both the constant and the mutable. This makes wonder if there is at all a simple and beautiful language that would at the same time allow the constant and non-constant types to live peacefully together...
>
> -A

    As I (hopefully) showed you, the immutability issue is very tricky. Once
we assume that everything can mutate we need to state everything that won't
change everytime. There are lots of hard work on this area, some propose a
"mutates" clause on methods, to define which object's parts will be
different after a method call. My test of Eon features was writing libraries
with the proposed syntax and semantics. When Eon had side-effects as the
norm and keywords to ensure immutability the code become very, very verbose.
Once I dropped the side-effects, I could concentrate on behaviour and
isolate the changes in Monads. IME it's almost impossible to come with a
readable, concise and comprehensive notation for immutability vs.
mutability.

    Best regards,
    Daniel Yokomiso.

"I didn't attend the funeral, but I sent a nice letter saying I approved of
it."
 - Mark Twain


---
Outgoing mail is certified Virus Free.
Checked by AVG anti-virus system (http://www.grisoft.com).
Version: 6.0.512 / Virus Database: 309 - Release Date: 20/8/2003


August 26, 2003
"Daniel Yokomiso" <daniel_yokomiso@yahoo.com.br> wrote in message news:bifhh6$1hb7$1@digitaldaemon.com...
>     Therefore I think that separating the immutability issue of the array
> vs. the element type leads to different semantics. Here we assume that
> "immutable A" is a supertype of "A".

This seems logical.

>     Immutable operations ("T immutable get(int)" and "int immutable
> length()") won't have side effects, but they may be changed by other
> objects/threads. But if you have a value type (like a number, matrix,
etc.),
> a immutable struct type, you'll be safe, because the value isn't shared. Immutability tell us nothing about aliasing.

Not having side effects allows one set of optimizations.

I would like the language to include something like "values changed by other threads *can* show up as changed to a thread which uses those values, however, changes are not *guaranteed* to show up unless the values are marked volatile."

This would allow the compiler to keep more values in registers.

Not sure what to do about called functions.  There is something extremely nice about knowing for sure that nothing will ever modify a structure.

>     The problem in C++ is that they use "a[0] = 1;" meaning, get the
address
> of the first element and let it be assignable, so you need two versions of array_get, one yielding const references and other yielding normal ones.
IMO
> "a[0]" should call array_get and "a[0] = 1" should call array_set, just syntatic sugar.

Basically properties.

>     But the issue here is that immutability, when you're dealing with
> generic types, generates some dependency between the parameters and the
> result types. Sometimes only one parameter will define the const issue,
> sometimes more than one. If you make a templated function to find
> statistical results from a array (min, max, average, etc.) you need a way
to
> ensure that these functions won't: mutate the array, won't mutate the
array
> elements, the return types will have the same immutability status (yes or no) that the array element type. In your example the TMin version for mutable parameters allowed the min function to change them through any mutating methods they had.

This is what I don't understand.  Why should Min return an immutable value? Perhaps it can return a copy of a value that can be mutated.  That's probably lower performance though.  Is this the only reason?

Sean



August 26, 2003
>
>     But the issue here is that immutability, when you're dealing with
> generic types, generates some dependency between the parameters and the
> result types. Sometimes only one parameter will define the const issue,
> sometimes more than one. If you make a templated function to find
> statistical results from a array (min, max, average, etc.) you need a way
to
> ensure that these functions won't: mutate the array, won't mutate the
array
> elements, the return types will have the same immutability status (yes or no) that the array element type. In your example the TMin version for mutable parameters allowed the min function to change them through any mutating methods they had.
>
>
> > >     In my language I decided to drop side-effects because: it's very
> > > expensive to track immutability and to provide two versions of the
same
> > > operations (e.g. a put method that changes the collection and a put
> method
> > > that returns a new colection with the new value added). Also it'd very
> hard
> >
> > I'd assume that you wouldn't need two versions; if you only want to have an immutable and a mutable version of a class, just drop the procedures and leave the pure methods in the immutable version -- calling the procedure on the immutable version would be prohibited at compile time, nice and clean.
> >
> > Maybe you wanted something completely different?
>
>
>     I was talking about the issue I mentioned in my last (statistical)
> example. You need to write things like:
>
>
> template stats(T) {
>     const_if_array_type_is_const T find_min(const T [const] array) { ... }
> }
>
>
>     That can be very awkward. The problem here is: the result type
> immutability depends on the array element type immutability, but we must
be
> able to ensure that the array element type won't be changed by the
function.
> These examples may be simple, but when you have more classes, it's hard to track immutability dependencies. Also immutability is one level only:
>
>

maybe something like that would be OK:

template stats(T)
{
    constnessof(array) T find_min(const T [const] array)
}

where we can indicate what to check for constness. The
list can include this for member functions and for complex type
we might allows a syntax similar to the one used for specialisation

    constnessof(array : pattern)

where patern would include the optional const

or alternatively a syntax where we could name modifier: For the sample I will uses # symbol. There are probably better alternative. Makes your suggestions!!!

template stats(T)
{
    constnessof(c1, c2) T find_min(const#c1 T [const#c2] array)
}

For non-virtual methods, we could support automatic constness detection which should not be hard for the compiler to implement (about the same complexity as generating an error in C++ when constness is not respected).

template stats(T)
{
    autoconst T find_min(const T [const] array)
}

where const would be added if required.

This would be usefull for generic code like the TMin template
or for containers that wants to return constant reference to their
elements if they are themself const...


Automatic modifier would also have other uses that for constness support. They would be usefull for function calling convention for example (I don't know how those specifier are handled in D - that is the equivalent of cdecl, fastcall, pascal and similar in C++).

For ex. in C++, mem_fun and related classes and functions only works when the default calling convention is used... but ideally the same code should be usable with any calling convention.


August 26, 2003
Antti Sykäri <jsykari@gamma.hut.fi> wrote in news:slrnbk5c5l.4q6.jsykari@pulu.hut.fi:

> Java, a blatant mockery of static typing, solves the "const" by providing a view to the container (via java.util.Collections.unmodifiableCollection or similar) which looks exactly like the original, but throws an UnsupportedOperationException when one tries to modify it.  No wonder there's a project trying to bring "const" to Java (http://pag.lcs.mit.edu/constjava/). [...]

ConstJava looks like a straight adaption of C++'s const notion. They just added the useful notion (for Strings etc.) of const classes.

The paper (http://pag.lcs.mit.edu/constjava/ConstJava.pdf) talks about a runtime exception for const_casts, but I didn't find any details. Wonder how this works.



Farmer.



August 27, 2003
Ok, let's drop immutable and just talk about const.

In article <bifhh6$1hb7$1@digitaldaemon.com>, Daniel Yokomiso wrote:
>     But the issue here is that immutability, when you're dealing with
> generic types, generates some dependency between the parameters and the
> result types. Sometimes only one parameter will define the const issue,
> sometimes more than one. If you make a templated function to find
> statistical results from a array (min, max, average, etc.) you need a way to
> ensure that these functions won't: mutate the array, won't mutate the array
> elements, the return types will have the same immutability status (yes or
> no) that the array element type. In your example the TMin version for
> mutable parameters allowed the min function to change them through any
> mutating methods they had.
> 

[snip]

>> Maybe you wanted something completely different?
> 
> 
>     I was talking about the issue I mentioned in my last (statistical)
> example. You need to write things like:
> 
> 
> template stats(T) {
>     const_if_array_type_is_const T find_min(const T [const] array) { ... }
> }
> 
> 
>     That can be very awkward. The problem here is: the result type
> immutability depends on the array element type immutability, but we must be
> able to ensure that the array element type won't be changed by the function.

Okay, now I think I have an idea about the fundamental problem. The min() function can be used as an example of the general idea. The function must return one of its arguments, whether mutable or immutable. At the same time it must provide the guarantee that it won't modify its arguments -- we'd like the type system to ensure that the only thing that "min(T, T)" is allowed to do is to pass one of its arguments back to the caller.

One way to ensure this is to make its arguments const; but then you'd have to make its return type const, as well, or the following wouldn't compile: (Let's leave the bodies away -- everybody probably knows what "min" looks like if he or she has read this far :)

I'll drop the template syntax because it has nothing to do with const really. Assume that T is an int or actually, reference type would be better to illustrate the const semantics.

const T min(const T x, const T y);

This is inacceptable, because sometimes we'd like to use min on non-const arguments; therefore we could solve the problem with overloading:

T min(T x, T y);
const T min(const T x, const T y);

And we need to have C++-like rules to disambiguate between the calls, or maybe now we could have some use for templates:

implicit template function (const T) {
    const T min(const T x, const T y);
}

implicit template function (T) {
    T min(T x, T y);
}

Another way would be unsafe and dirty but I'll still include it for completeness:

T min(const T x, const T y) {
    T mutable_x = const_cast<T>(x), mutable_y = const_cast<T>(y);
    return x < y ? mutable_x : mutable_y;
}

However, this would require the caller to promise not to touch the result if one of the originals were const in the first place. Ok, pretty ugly idea but its actually a prelude to this solution:

Don't use "const" as a type modifier at all, but merely as a way of documenting the function's behavior (as in "I won't change this parameter"). Enter contracts. Like so:

T min(const T x, const T y) {
    // implementation...
}
out() {
    // mathematical properties of min(x,y)
    assert(result <= x);
    assert(result <= y);
    // it's one of them
    assert(result === x || result === y);
    // I didn't change my parameters, I promise; these would be
    // generated by "const"
    assert(old x == x);
    assert(old y == y); // Here we would actually have to rely on
    // deep_clone or similar, as you mentioned below. Tricky notion,
    // comparing something that exists now to something that existed
    // earlier in time
}

Side note: Don't you just love when the function's postconditions actually describe perfectly what the function does! Reminds of me of the good old combinator function in purely functional languages. It takes a function of type ('a -> 'b) and a function of type ('b -> 'c) and returns a function of type ('a -> 'c). Now what could a function of this type do? The notation "'something" means generic type parameter, so it cannot assume anything about its types.  Well, what else can it do, then, than create a function that first calls the first function and then calls the second function on the result? So the function's type is a complete description of the function itself.

Back to the min() problem. Ideas only keep getting wilder... well, do you remember how we did it in C times: Macro! Of course!

#define min(x, y) ((x) < (y) ? (x) : (y))

This actually works better (except for the side-effects of call-by-name parameter passing) than any of its C++ equivalents, since you can do things like

int x = 5;
const float y = 1.0;

float z = min(x, y);

No need for different "const" methods, overloading or anything! Amazing! Compare this with what Andrei Alexandrescu did to get the (nearly) similar effect with templates:

http://www.cuj.com/documents/s=7996/cujcexp1904alexandr/

By the way, he has an interesting comment: "Minimum and maximum are two simple concepts present both in math and in the real life.  If a programming language is unable to express simple concepts of math and life, then there is something deeply wrong with that language."

Anyway - macros seem to work. Why is this possible? Well, because min() is not an actual function call but merely a textual substitution. Well, what language feature was introduced in C++ (and later, in C) to get rid of a certain usage of macros?

Taking Andrei's challenge, let's bring forth the final solution: inline functions.

I wouldn't mean to use inline functions as a means to perform premature optimization, a purpose which programmers are often accused of misusing. But I simply would want to take advantage of the fact that inline functions can "see" the context where they are used, and therefore they would also be able to provide different kinds of code than usual functions. Actually, after an inline function were expanded several times, the compiler could gather them to a single (if possible) function and optimize the inlined code to a function call :)

One thing that inline functions could provide would be determining the
return type based on argument types. In fact, the inline function could
work directly on the symbolic environment of the caller;
assume

inline Symbol min(Symbol x, Symbol y) {
    return x < y ? x : y;
}

This would expand similarly to the macro:
void g(int x, float y) {
    float z = min(x, y); // expands into:
    // float z = x < y ? x : y;
}

It would have to have different specialisations; for non-symbol expressions, the following would be expanded:

inline T min(T x, T y) {
    // as usual
}

Inline functions would of course have to have some kind of template-like implicit instantiation mechanism (which D templates is soon going to get, I hope) and ability to specialize on different types. And they could naturally be used to implement variable-sized argument lists type-safely...

Hmm, I think I got sidetracked a bit. Could be time for sleep.

>      Some issues regarding mutability are discussed here
><http://lambda.weblogs.com/discuss/msgReader$8378?mode=topic&y=2003&m=8&d=25
>> Tim Sweeney has some pretty good thoughts.

An interesting discussion. BTW, the guy who he was talking with, Vesa Karvonen, I used to work with some time ago. He's a smart guy; taught me a lot about programming. It's a small world, and particularly small in the area of functional languages and type theory :)

>> Uh, could you give an example of one hard case?
> 
>     Sorry I meant: 'Also it's very hard to write "ensure" clauses on
> contracts of methods that have side-effects (i.e. methods that mutate the
> instance)'.
> 
> Set(T) {
>     void remove(T item)
>     out () {
>         if (old this.contains(item)) {
>             assert(this.size + 1 == (old this.size()));
>         }
>         assert(!this.contains(item));
> // so far so good.
>        for (T each; old this) {
>            if (each != item) {
>                assert(this.contains(item));
>            }
>        }
> /* ops, it won't work because the "old this" expression will yield a
> reference
>  * to the this object, and here it's already mutated. The correct expression
> is
>  * "(old this.clone())" ensuring that we get a snapshot of the this object
> from
>  * before the method execution.
>  */
>     };
> }

Well, that's what you get if you really want to compare two sets, only one of which is available at a time. I'd really just leave it, settle with simpler preconditions and place some trust on the programmer...

I wouldn't find very complicated examples in Eiffel's libraries, either; in particular, no clone()'s in old expressions. I looked at, for example, the equivalent BINARY_SEARCH_TREE_SET at

http://docs.eiffel.com/libraries/base/reference/structures/set/binary_search _tree_set.html#f_prune

which has the similar postcondition

removed_count_change: old has (v) implies (count = old count - 1)

but does not have the last check.


-Antti