November 14, 2006
Walter Bright wrote:
> Tomas Lindquist Olsen wrote:
>> Can you post an example of why we need 'static const' ?
> 
> int foo(int x)
> {
>     static const i = x;    // error, x is not const
>     const j = x;    // ok, j is const for duration of foo() call
> }

Confusing.. I thought const in D meant really 'constant'. If a const in a function can have different values across calls, then I suppose a const for a class can have different value for different instances too?

L.
November 14, 2006
Kristian Kilpi wrote:
> On Mon, 13 Nov 2006 21:17:32 +0200, Bill Baxter <wbaxter@gmail.com> wrote:
> 
>> Kristian Kilpi wrote:
>>> On Mon, 13 Nov 2006 20:40:23 +0200, JC <johnch_atms@hotmail.com> wrote:
>>
>>>>> Also I don't like this syntax. All variables should always be declared  at
>>>>> the begining of blocks.
>>>>
>>>>
>>>> According to whom? That's a style thing, not a rule.
>>>>
>>>  Yes, that was just my humble opinion. :)
>>>  I just think that it would be consistent if all the variables will be  declared in the same place.
>>
>> You're free to have your opinion, but the C++ books I've read disagree with you.    The ones I've read advocate declaring variables as close to the point of usage as possible.  (Can't remember which books exactly, but probably Stroustrup and/or Meyers' books.)
>>
>> --bb
> 
> Hehheh. Ok, I think I understant the point behind that. Well, I guess both the styles have their own advantages and disadvantages. The style where one declares (almost) all the variables at the begining of the (main) block works for me. Sorry for assuming that this style is the Correct Way(TM)... ;)

It used to be the only way that worked. Early C compilers couldn't cope with variables declared elsewhere. That restriction is long gone, now.
November 14, 2006
Lionello Lunesu wrote:
> Confusing.. I thought const in D meant really 'constant'. If a const in a function can have different values across calls, then I suppose a const for a class can have different value for different instances too?

Yes.
November 14, 2006
== Quote from Sean Kelly (sean@f4.ca)'s article
>      MyClass c = scope MyClass();
>      MyClass c = new(scope) MyClass();
> Both would allow data to be allocated on the stack without escape
> detection (or an assumption that the user isn't being evil), and the
> latter is even almost identical to how alloca is used now.

I like the above suggestions. After thinking about it, I'm beginning to wonder if scoped variables are usefull at all. The way I see it, there are three cases, where one might desire scoped variables:

1. Guaranteed, deterministic finalization (mostly disposal of system resources).
For example, file should be closed when going out of scope. In this case, it would
be better to make File class scoped, not individual variables:
    auto/scoped/whatever class File {
        this() { ... }
        ~this() { close; }
    }

2. Avoiding garbage collection. This can be accomplished, for example, with custom
allocation (stack, for example). We can solve this by allowing a single parameter
of type DeterministicAllocator to 'new' keyword in all cases (even if 'new' is not
overriden in class). Maybe Allocator should be defined as a 'void* delegate()'.
For example:
    // defined in Phobos and treated as special by compiler
    typedef void* delegate() DeterministicAllocator;

    class Foo {
        // 'new' is NOT defined here
    }
    void test() {
        DeterministicAllocator myOwnAllocator = stackAllocator;
        Foo foo = new(myOwnAllocator) Foo();
    }

3. Some aspects of 'value class' semantics are required. Note that 'scope' or
'auto' didn't solve this problem. The best way to solve this is to make structs
more powerful (allow inheritance, possibly even interface implementation, default
constructors, destructors, but do NOT allow virtual/overriden methods). For example:
    struct BigInteger: BigCardinal {
        ~this() {
            dispose;
        }
        bool negative;
    }

In either case, we don't really need scoped variables at all.
November 14, 2006
== Quote from Boris Kolar (boris.kolar@globera.com)'s article
> 2. Avoiding garbage collection. This can be accomplished, for example, with custom
> allocation (stack, for example). We can solve this by allowing a single parameter
> of type DeterministicAllocator to 'new' keyword in all cases (even if 'new' is not
> overriden in class). Maybe Allocator should be defined as a 'void* delegate()'.
> For example:
>     // defined in Phobos and treated as special by compiler
>     typedef void* delegate() DeterministicAllocator;
>     class Foo {
>         // 'new' is NOT defined here
>     }
>     void test() {
>         DeterministicAllocator myOwnAllocator = stackAllocator;
>         Foo foo = new(myOwnAllocator) Foo();
>     }

Of course, I forgot about deallocaion, so allocators would be defined as something like:

// somewhere in Phobos ...
typedef void* delegate() Allocate;
typedef void delegate(void*) Deallocate;

struct NormalAllocator {
    Allocate allocate;
    Deallocate deallocate;
}
struct DeterministicAllocator {
    Allocate allocate;
    Deallocate deallocate;
}

// in application
DeterministicAllocator stack = deterministicStack;
void test() {
    Foo foo = new(stack) Foo();
    // foo is destroyed here, because stack is DeterministicAllocator
}
November 14, 2006
On Sun, 12 Nov 2006 20:59:05 +0200, Walter Bright <newshound@digitalmars.com> wrote:

> Carlos Santander wrote:
>> I don't think they're valid concerns (meaning they're subjective, not everyone will have those ideas), but I think we (the D community) just want a way to clearly differentiate both meanings of auto. So, choose any two words: auto/scope, var/auto, def/scoped, foo/bar, and let's move on.
>
> I think the auto/scope is probably the best idea.
>
>> Also keep in mind that your option #5 (auto c = Class()) doesn't seem to be a popular proposal (based on what has been said in the ng).
>
> I'm a bit surprised at that, but the negative reaction to it is pretty clear.

Well I kind of like it. Unfortunate it breaks the static opCall, so it would be too confusing to have I think. E.g.

  Class b;
  auto c = Class();

  b = Class();  //wha? calls static opCall?


The following (shorthand) notation won't have that problem:

  Class c();

Later new values cannot be assigned to 'c':

  Class c(10, 2);

  c = new Class;  //error

You could also use this notation with classes:

  class Foo {
    int v = 1;
    Bar b(10);
  }

'b' is binded to 'Foo', it gets destroyed with it. This notation would remove the initialization of objects from the constructors to declaration lines (a nice ability to have sometimes). Which is consistent with initialization of non-object variables.

I think this would be great to have as a shorthand, an *alternative* way to create scoped/binded objects.
November 14, 2006
Derek Parnell wrote:
> On Sat, 11 Nov 2006 10:48:00 -0800, Walter Bright wrote:
> 
>> The auto storage class currently is a little fuzzy in meaning, it can mean "infer the type" and/or "destruct at end of scope". The latter only has meaning for class objects, so let's look at the syntax. There are 4 cases:
>>
>> class Class { }
>>
>> 1) auto c = new Class();
>> 2) auto Class c = new Class();
>> 3) auto c = some_expression();
>> 4) auto Class c = some_expression();
>>
>> The ambiguity can be resolved by saying that if auto is used for type inference, i.e. cases (1) and (3), then it does not mean RAII. If it is not used for type inference, i.e. cases (2) and (4), then it does mean RAII.
>>
>> In the future, I'd like the following to work:
>>
>> 5) auto c = Class();
>>
>> which would mean type inference *and* RAII.
> 
> Please excuse my bash and amateur crashing in here ... but Walter, for a
> very intelligent person, why is it that you just don't get it?
> 
> Type inference and RAII are two distinct, unrelated and orthogonal
> concepts. Using one keyword in subtly different syntax permutations to
> indicate both concepts is just plain stupid.
> 
> In each of the suggested permutations above, the code reader and writer
> will constantly be thinking "now what's the precise syntax again?"  This
> reminds me of the exact criticism that you have leveled against some C++
> constructs, and some syntax changes to D suggested by your supporters.
> Namely that the subtle differences in syntax variants is a cause of bugs
> and makes writing code harder - because they are not visually distinct
> enough for the average person.
> 

I *fully* agree. It comes a few days late, but I still wanted to say it. The irony and mischeviousness of the statement "The auto storage class currently is a *little fuzzy* in meaning" is teeth-grinding(or something). Walter, I hope that by judging the response in this thread you've seen that everyone agrees that these two language concepts should be well separated. There is really no discussion on that. The question to discuss is rather *how* that separations should be (which keywords, etc.).

-- 
Bruno Medeiros - MSc in CS/E student
http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
November 14, 2006
Proposed extensions, that should solve most RAII (and other) problems:

1 Structs
=========


Structs are a lot like classes, except they can NOT:
- have virtual methods
- override inherited methods
(technically: they can't have VMT table)


1.1 Immutable structs
---------------------

In addition to normal structs, immutable structs can be defined with:

  const struct Point {
    int x;
    int y;
  }

They can be constructed as:

  void test() {
    Point p = Point(10, 20); // x = 10, y = 20
    Point p = Point(x: 10, y: 20); // same as above
    p.x = 30; // COMPILER ERROR: Point is immutable
  }


1.2 Structs can have constructor(s) and destructor
--------------------------------------------------

Also, structs may have constructor(s), destructor:

  struct File {
    this() {
      printf("File.this");
    }
    this(char[] name) {
      handle = openFile(name);
      // handle can be modified only in constructor
    }
    ~this() {
      closeFile(handle);
    }
    HANDLE handle;
  }

Invocation is deterministic (at the beginning and end of variable scope):

  void test() {
    File f; // prints "File.this" (default constructor is invoked)
    // destructor of f is called here
  }


1.3 Inheritance and implicit conversions of structs
---------------------------------------------------

Structs can inherit from one or more other structs:

  struct A {
    int a;
  }
  struct B {
    int b;
  }
  struct C: A, B {
    // inherited: int a;
    // inherited: int b;
    int c;
  }

Structs (and classes too) can define implicit conversions:

  // A may be struct, interface, class, ...
  struct D: A {
    // nothing is inherited because implicit conversion is defined
    this.A {
      return A(a: 10);
    }
  }

Inheritance and implicit conversion test:

  void test() {
    C c;
    B b = c; // allowed
    D d;
    A a = d; // allowed
  }


2 Classes
=========


2.1 Explicit implementation of interfaces
-----------------------------------------

Classes can explicitly implement interface methods or whole interfaces:

  interface IFoo {
    void foo();
  }
  interface IBar {
    void bar();
  }
  class Foo: IFoo, IBar {
    // explicitly implement IFoo interface methods
    void IFoo.foo() {
    }
    // explicitly implement interface IBar by delegation
    this.IBar {
      return getBar();
    }
  }


2.2 Value classes
-----------------

Value classes are like ordinary classes, except:
- only constructor or destructor can change class state
- they can not have null value (at least default constructor is called)
- they have deterministic finalization if they implement 'Local'

Example:

  const class Foo: Local {
    this() {
      foo = 5; // foo can be modified (only) in constructor/destructor
    }
    ~this() {
      printf("Foo.~this");
    }
    int foo;
  }
  void test() {
    Foo foo; // foo.foo = 5 (default constructor is invoked, foo can't be null)
    // prints "Foo.~this" and destroys foo here
  }

Value classes can easily support deterministic finalization, because no cycles are possible (without hacking, anyway) and simple reference counting is sufficient.


2.3 Custom allocators
---------------------

Custom allocators allow any allocation strategy:

  // in Phobos
  interface Allocator {
    void* allocate(Type type);
    void deallocate(Type type, void* mem);
  }
  // test application
  void test() {
    Foo foo = new(myAllocator) Foo();
    // if Foo is value class and implements 'Local', then foo is deleted here
    // ... else foo is deleted when myAllocator is deleted
  }


3 Summary
=========


It seems that scoped variables are unnecessary, because value classes that implement 'Local' interface, together with implicit conversions, are sufficient in all of the following scenarios:

1. Finalization is required:

  const class File: Local {
    ~this() {
      close;
    }
  }

2. Garbage collection must be avoided:

  const class Scoped(T): T, Local {
    this(T object) {
      _object = object;
    }
    this.T {
      return _object;
    }
    private T _object;
  }
  Scoped(T) scoped(T)(T object) {
    return new Scoped!(T)(object);
  }
  class Foo {
    void foo() { ... }
  }
  void test() {
    Foo foo = scoped(new Foo());
    foo.foo();
    // foo is deleted here :)
  }
  void test2() {
    scoped(new Foo()).foo();
    // Foo instance is deleted here too! :D
  }

3. Value class semantics is desired:

  const class Point {
    int x;
    int y;
  }

Another (minor) suggestion is allowing method declarations without '()':

  class Foo: Bar {
    int foo {
      // no ambiguity here, consistent with 'this.Bar' definition
      return 5;
    }
    this.Bar {
      return bar;
    }
    private Bar bar;
  }

Also, get rid of static opCall
November 14, 2006
Walter Bright wrote:
> Tomas Lindquist Olsen wrote:
>> Can you post an example of why we need 'static const' ?
> 
> int foo(int x)
> {
>     static const i = x;    // error, x is not const
>     const j = x;    // ok, j is const for duration of foo() call
> }

I get "main.d(4): non-constant expression x", with such code:

  int foo(int x)
  {
    //static const i = x;
    const j = x;  // non-constant expression x
    return 1;
  }


-- 
Bruno Medeiros - MSc in CS/E student
http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
November 14, 2006
Tomas Lindquist Olsen wrote:
> 
> a static in a class is well defined. so is a const. for me 'static const' reads as having the 'static' redundant. a 'const' in D is by definition static. It cannot change, as only a compile-time constant will qualify, and thus will be the same to all classes of whatever reads it.
> a function 'const' is very much the same. I fail to come up with a scenario where choosing the storage model for a constant makes any sense!
> Can you post an example of why we need 'static const' ?

Much like "auto", D's "const" is ambiguous, it represents two different concepts:
  compile time constant.
  read-only variable(assign only once), same as "final" in Java and C#.

You get the first with something like:
  const int foo = 2; // compile time constant
You get the second with:
  const int foo;
  this() {
    foo = 2;
  }
that is, the final-like-const variable (foo here) must be initialized in the constructor of the class/struct or module where it is a member of.


So, the compile-time-const does not make sense with "static", but the final-like-const does:

---
module test;

static const int foo;

static this() {
  foo = 42;
}

void test() {
  int a = foo; // Ok
  foo = 2;  // Error "can only initialize const foo inside constructor"
            // AKA foo is final
}

-- 
Bruno Medeiros - MSc in CS/E student
http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D