February 01, 2013
On Friday, 1 February 2013 at 04:33:05 UTC, Steven Schveighoffer wrote:
> On Thu, 31 Jan 2013 19:59:46 -0500, Zach the Mystic:
>> This is not some kind of special property implementation. n is a struct. It has no data and therefore is a very peculiar kind of struct. A struct with no data needs only one instance, ever. Copying it is pointless. Taking its address is pointless. Creating a new instance is pointless. Passing it by ref is pointless. All operations which require more than one instance are pointless, which is why it doesn't need a separately defined type. The type and the instance can therefore use the same name, n in this case.
>
> No, the struct must have data.  If it doesn't, how does it get back to the owner type?  In other words, your n struct must have a pointer to the myStruct instance it intends to modify _n on.

How does any function get hooked up with data? The compiler figures out what data is to be passed to which function. It's no different from how the compiler figures out how to pass data defined in one module to functions defined in a different module. Empty structs are just namespaces with powerful semantics. They have no pointers, unless they're nested in a struct with data, in which case they have the same pointer as any member function of that struct.

struct A
{
  int q = 23;
  void incrementQ() { ++q; }
}

How on earth could this function increment q when it's not even defined in the function?!?!? It must be a miracle. Oh, no wait, it needs a pointer to the struct in question. Duh.

There's no difference with data-less structs inside regular structs.

struct A
{
  int q;
  incrementQ struct
  {
    void opCall() { ++q; }
  }
}

Where's the need for some hocus-pocus mystery pointer here? The empty struct has no data to worry about. Functions inside the empty struct get the same damn pointer as the other functions in struct A. But of course, you can't do this:

struct B
{
  int _q;
  q struct
  {
    int opUnary(string s)() if (s == "++")
    {
      writeln("You know, I just wanted to have a conversation while I was busy incrementing q");
      ++_q;
      writeln("I did all sorts of stuff with the increment operator while I was at it");
      return _q;
    }
  }
}

...with normal function calls.

> Unless, of course, you pass the pointer to myStruct as the 'this' reference.

Ain't no "this" in an empty struct. Use "outer.this" instead.

>But then, this isn't a normal struct, and
> I'm really failing to see why we have to make this a struct at all.

Because it's already implemented, except for a few details, because it opens up possibilities for properties other languages could only dream of, and because it obviates the need for tags like @property to provide far weaker functionality.

>> Note that n uses the new rule which allows it access to its parent's scope, the same way it would in a nested function. The only pointer it needs is the same pointer used for myStruct, since myStruct actually stores some data.
>
> FYI, nested structs in functions (the ones you want to use as a model) have an extra hidden reference pointer back to the stack frame data.  That is how they can access the local variables of the function.

Yeah, see, the problem is, if empty structs, which require no "this" pointer to themselves, are allowed access to their parent scope, it suggests that all structs should do the same. Now we have one good syntax for these things : "foo struct { ... }", which could be fun to use in other places too (where you want a single instance of a struct). But if these empty structs are fundamentally different from regular structs, it will force a situation where the Highlander syntax must be used only on these "special" structs, to make sure the compiler knows the difference. It's a problem, to be sure.

> This proposal looks way too complicated.  I don't see how it's a win over the current state of affairs, or even the @property implementation we expected to get but didn't.  You need to solve the "owner pointer" problem before it's even viable.
>
> -Steve

You say it's complicated, but I ask you this: does any other proposal completely eliminate the so-called eye-sore "@property" while also providing functionality with unknown potential which goes far beyond what people are used to? And the owner pointer problem is only a problem if we want to make the language complete and consistent with regard to normal non-static structs holding an outer struct pointer. I think having a use case for this type of struct would seal the deal, but as it is, I'm not sure.

February 01, 2013
On Friday, 1 February 2013 at 04:22:51 UTC, Zach the Mystic wrote:
> On Friday, 1 February 2013 at 03:39:19 UTC, TommiT wrote:
>> On Friday, 1 February 2013 at 03:13:42 UTC, Zach the Mystic wrote:
>>> On Friday, 1 February 2013 at 02:27:30 UTC, TommiT wrote:
>>>> And here's another example of why it is as big of a problem as I make it sound to be:
>>>>
>>>> import std.concurrency;
>>>>
>>>> struct Array
>>>> {
>>>>  int _len;
>>>>
>>>>  length struct // Using Zach's syntax
>>>>  {
>>>>      @property get() { return _len; }
>>>>      alias this = get;
>>>>      void opAssign(int rhs) { _len = rhs; }
>>>>  }
>>>> }
>>>>
>>>> void func(T)(T t)
>>>> {
>>>>  auto v = t;
>>>>  v = 10;
>>>> }
>>>>
>>>> void main()
>>>> {
>>>>  Array arr;
>>>>  spawn(&func!int, arr.length);
>>>>  arr.length = 20; // And here we have a data-race.
>>>>                   // Good luck trying to find these
>>>>                   // kinds of bugs.
>>>> }
>>>
>>> I'm sorry, I know very little about race conditions. If you might explain just a little bit about what is happening here, I'd be in a better position to understand what you're saying. I really can't say anything other than please describe what this does and why it's a problem at this time.
>>
>> spawn(&func!int, arr.length);
>>
>> ...creates a new thread and runs the following function call in it:
>> func!(int)(arr.length)
>>
>> While that function call is evaluating in the new thread, the old thread may simultaneosly execute:
>> arr.length = 20;
>>
>> Since both of those threads end up at some point calling arr.length.opAssign and therefore both of them may assign to arr._len simultaneously, that's a data-race. It would be data-race also if only one of them wrote to it and the other one just read it.
>
> I'm familiar with the fact that programmers face endless woes with regard to data-races. I thought that D has unshared data by default. But since Array arr is defined in main instead of outside main, therefore it is sharable between the two threads? If two threads have access to the same data, what makes length as a property different from length as an int? This problem may be over my head for now...

Actually, that example of mine was wrong - it's not a data-race. I failed to notice that in:
spawn(&func!int, arr.length);
...arr.length is actually converted to int using alias this. My bad.

Now I'm going to repeat the "spooky modification at a distance" problem I showed earlier, but this time, instead of writing it in the new syntax and the new struct behaviour you invented, I write it using current D syntax:

import std.concurrency;

struct Array
{
    int _len;

    struct PropType
    {
        Array* _outer; // In your syntax this was implicit

        @property int get()
        {
            return _outer._len;
        }

        alias this = get;

        void opAssign(int rhs)
        {
            _outer._len = rhs;
        }
    }

    PropType length;
}

void func(T)(T t)
    if (isImplicitlyConvertible!(T,int))
{
    // Given that is(T == Array.PropType), then
    // all 3 lines below do the same thing:
    t = 42;
    t.opAssign(42);
    t._outer._len = 42;
}

void main()
{
    Array arr;

    // setting the _outer pointer to the enclosing object:
    arr.length._outer = &arr;

    assert(arr._len == 0);
    func(arr.length); // no conversion to int happening here
    assert(arr._len == 42);
}

So, it's possible to modify arr._len just by passing arr.length by value to a function.
February 01, 2013
On Friday, 1 February 2013 at 06:52:32 UTC, Zach the Mystic wrote:
>> FYI, nested structs in functions (the ones you want to use as a model) have an extra hidden reference pointer back to the stack frame data.  That is how they can access the local variables of the function.
>
> Yeah, see, the problem is, if empty structs, which require no "this" pointer to themselves, are allowed access to their parent scope, it suggests that all structs should do the same. Now we have one good syntax for these things : "foo struct { ... }", which could be fun to use in other places too (where you want a single instance of a struct). But if these empty structs are fundamentally different from regular structs, it will force a situation where the Highlander syntax must be used only on these "special" structs, to make sure the compiler knows the difference. It's a problem, to be sure.

What I mean is, that the annoying aspect of making struct-nested structs act like their function-nested struct counterparts is that the part which is most difficult to implement will actually be the part people use the least. The main need is for data-less struct-nested structs to have access to their parent's scope, but implementing struct-nested structs which actually hold data will be more annoying, but it will be necessary for language consistency. But I though of a rather crude temporary fix. Simply disallow non-static struct-nested structs which actually hold data of their own. With this error built-in, all programmers can now tag all of their struct-nested structs with "static" until their program compiles once more. Now the language is clean and ready for the rather easy addition of data-less nested structs, and the slightly trickier data-ful struct-nested structs to be implemented when the developers get the chance.

All structs now will behave the same way, freeing up the Highlander syntax for use on *any* struct, data-less or data-ful.
February 01, 2013
On Fri, 01 Feb 2013 01:52:29 -0500, Zach the Mystic <reachBUTMINUSTHISzach@googlymail.com> wrote:

> On Friday, 1 February 2013 at 04:33:05 UTC, Steven Schveighoffer wrote:
>> No, the struct must have data.  If it doesn't, how does it get back to the owner type?  In other words, your n struct must have a pointer to the myStruct instance it intends to modify _n on.
>
> How does any function get hooked up with data? The compiler figures out what data is to be passed to which function. It's no different from how the compiler figures out how to pass data defined in one module to functions defined in a different module. Empty structs are just namespaces with powerful semantics. They have no pointers, unless they're nested in a struct with data, in which case they have the same pointer as any member function of that struct.
>
> struct A
> {
>    int q = 23;
>    void incrementQ() { ++q; }
> }
>
> How on earth could this function increment q when it's not even defined in the function?!?!? It must be a miracle. Oh, no wait, it needs a pointer to the struct in question. Duh.

No need to get snippy :)  Especially when you are wrong.

Try this:

struct A
{
   int q;
   struct B
   {
      void foo() {q++;}
   }
}

Won't compile.  That's becuase foo is passed a reference to the A.B struct, not the A struct.

If you want it to compile, B will *necessarily* have to have a pointer to A.

If you want B's methods to be passed A's pointer, then this is not a struct.  Plain and simple.  It's just a namespace, the "just like any other struct" is meaningless, since it isn't.

Now, try this:

struct S {}

pragma(msg, sizeof(S).stringof); // outputs 1 (empty structs must have some size).

void foo()
{
  int q;
  struct A
  {
     void foo() {q++;}
  }

  pragma(msg, sizeof(A).stringof); // outputs 4 (or 8 on 64-bit machines)

}

Why?  Because the reason those "miracle" nested structs work is because they have a hidden context pointer.

Even empty structs have size of 1 byte because they must have a 'this' pointer.

>
> There's no difference with data-less structs inside regular structs.
>
> struct A
> {
>    int q;
>    incrementQ struct
>    {
>      void opCall() { ++q; }
>    }
> }
>
> Where's the need for some hocus-pocus mystery pointer here? The empty struct has no data to worry about. Functions inside the empty struct get the same damn pointer as the other functions in struct A.

Then this is not a normal struct, in fact it has nothing to do with a struct.

> But of course, you can't do this:
>
> struct B
> {
>    int _q;
>    q struct
>    {
>      int opUnary(string s)() if (s == "++")
>      {
>        writeln("You know, I just wanted to have a conversation while I was busy incrementing q");
>        ++_q;
>        writeln("I did all sorts of stuff with the increment operator while I was at it");
>        return _q;
>      }
>    }
> }
>
> ...with normal function calls.

Certainly you can:

struct B
{
   int _q;
   @property auto q()
   {
      static struct incrementer
     {
       int *_q;
       int opUnary(string s)() if (s == "++")
       {
         writeln("You know, I just wanted to have a conversation while I was busy incrementing q");
         ++(*_q);
         writeln("I did all sorts of stuff with the increment operator while I was at it");
         return *_q;
       }
     }
     return incrementer(&_q);
   }
}

Although, because of dumb rvalue/lvalue rules (apparently, incrementer is not an lvalue, so can't have ++ called on it), this doesn't actually compile...

Through some finagling, I can get this to compile, but it's not usable with this compiler bug:

import std.stdio;

struct B
{
    int _q;

    struct incrementer
    {
        int *_q;
        int opUnary(string s)() if (s == "++")
        {
            writeln("You know, I just wanted to have a conversation while I was busy incrementing q");
            ++(*_q);
            writeln("I did all sorts of stuff with the increment operator while I was at it");
            return *_q;
        }
    }

    private incrementer qinc; // needed to make incrementer an lvalue.

    @property ref incrementer q()
    {
        qinc._q = &_q;
        return qinc;
    }
}

void main()
{
    B b;
    assert(b._q == 0);
    ++b.q; // b.q++ doesn't work, another bug
    assert(b._q == 1);
}

But I think actually, if we are going to define get and set in C# style, it would be useful to be able to define all the operators.  So that part of the plan has merit.

I think you need to drop the struct moniker.  This is not a struct.  You do that, and I think your proposal is on more solid ground.

>
>> Unless, of course, you pass the pointer to myStruct as the 'this' reference.
>
> Ain't no "this" in an empty struct. Use "outer.this" instead.

outer is the pointer I am referring to.  It's not magic, it must come from somewhere.  If the struct has no this pointer, it's not a struct.  A data-less struct *STILL* has a this pointer.

>
>> But then, this isn't a normal struct, and
>> I'm really failing to see why we have to make this a struct at all.
>
> Because it's already implemented, except for a few details, because it opens up possibilities for properties other languages could only dream of, and because it obviates the need for tags like @property to provide far weaker functionality.

Hand waving doesn't solve the problems.  The details are important to resolve.

What it seems like you have done is hijacked the 'struct' keyword for a property.  It isn't a struct, and it doesn't obviate the need for a tag.

> You say it's complicated, but I ask you this: does any other proposal completely eliminate the so-called eye-sore "@property" while also providing functionality with unknown potential which goes far beyond what people are used to? And the owner pointer problem is only a problem if we want to make the language complete and consistent with regard to normal non-static structs holding an outer struct pointer. I think having a use case for this type of struct would seal the deal, but as it is, I'm not sure.

What I mean by complicated is that it seems like a lot more work than it should be.

If we want to define new syntax to define properties, let's do it in a way that is concise and to the point.  I don't want to make properties look like structs, they are not structs.

-Steve
February 01, 2013
On Friday, 1 February 2013 at 15:29:38 UTC, Steven Schveighoffer wrote:
> On Fri, 01 Feb 2013 01:52:29 -0500, Zach the Mystic <reachBUTMINUSTHISzach@googlymail.com> wrote:
>
>> On Friday, 1 February 2013 at 04:33:05 UTC, Steven Schveighoffer wrote:
>>> No, the struct must have data.  If it doesn't, how does it get back to the owner type?  In other words, your n struct must have a pointer to the myStruct instance it intends to modify _n on.
>>
>> How does any function get hooked up with data? The compiler figures out what data is to be passed to which function. It's no different from how the compiler figures out how to pass data defined in one module to functions defined in a different module. Empty structs are just namespaces with powerful semantics. They have no pointers, unless they're nested in a struct with data, in which case they have the same pointer as any member function of that struct.
>>
>> struct A
>> {
>>   int q = 23;
>>   void incrementQ() { ++q; }
>> }
>>
>> How on earth could this function increment q when it's not even defined in the function?!?!? It must be a miracle. Oh, no wait, it needs a pointer to the struct in question. Duh.
>
> No need to get snippy :)  Especially when you are wrong.
>
> Try this:
>
> struct A
> {
>    int q;
>    struct B
>    {
>       void foo() {q++;}
>    }
> }
>
> Won't compile.  That's becuase foo is passed a reference to the A.B struct, not the A struct.
>
> If you want it to compile, B will *necessarily* have to have a pointer to A.
>
> If you want B's methods to be passed A's pointer, then this is not a struct.  Plain and simple.  It's just a namespace, the "just like any other struct" is meaningless, since it isn't.
>
> Now, try this:
>
> struct S {}
>
> pragma(msg, sizeof(S).stringof); // outputs 1 (empty structs must have some size).
>
> void foo()
> {
>   int q;
>   struct A
>   {
>      void foo() {q++;}
>   }
>
>   pragma(msg, sizeof(A).stringof); // outputs 4 (or 8 on 64-bit machines)
>
> }
>
> Why?  Because the reason those "miracle" nested structs work is because they have a hidden context pointer.
>
> Even empty structs have size of 1 byte because they must have a 'this' pointer.
>
>>
>> There's no difference with data-less structs inside regular structs.
>>
>> struct A
>> {
>>   int q;
>>   incrementQ struct
>>   {
>>     void opCall() { ++q; }
>>   }
>> }
>>
>> Where's the need for some hocus-pocus mystery pointer here? The empty struct has no data to worry about. Functions inside the empty struct get the same damn pointer as the other functions in struct A.
>
> Then this is not a normal struct, in fact it has nothing to do with a struct.
>
>> But of course, you can't do this:
>>
>> struct B
>> {
>>   int _q;
>>   q struct
>>   {
>>     int opUnary(string s)() if (s == "++")
>>     {
>>       writeln("You know, I just wanted to have a conversation while I was busy incrementing q");
>>       ++_q;
>>       writeln("I did all sorts of stuff with the increment operator while I was at it");
>>       return _q;
>>     }
>>   }
>> }
>>
>> ...with normal function calls.
>
> Certainly you can:
>
> struct B
> {
>    int _q;
>    @property auto q()
>    {
>       static struct incrementer
>      {
>        int *_q;
>        int opUnary(string s)() if (s == "++")
>        {
>          writeln("You know, I just wanted to have a conversation while I was busy incrementing q");
>          ++(*_q);
>          writeln("I did all sorts of stuff with the increment operator while I was at it");
>          return *_q;
>        }
>      }
>      return incrementer(&_q);
>    }
> }
>
> Although, because of dumb rvalue/lvalue rules (apparently, incrementer is not an lvalue, so can't have ++ called on it), this doesn't actually compile...
>
> Through some finagling, I can get this to compile, but it's not usable with this compiler bug:
>
> import std.stdio;
>
> struct B
> {
>     int _q;
>
>     struct incrementer
>     {
>         int *_q;
>         int opUnary(string s)() if (s == "++")
>         {
>             writeln("You know, I just wanted to have a conversation while I was busy incrementing q");
>             ++(*_q);
>             writeln("I did all sorts of stuff with the increment operator while I was at it");
>             return *_q;
>         }
>     }
>
>     private incrementer qinc; // needed to make incrementer an lvalue.
>
>     @property ref incrementer q()
>     {
>         qinc._q = &_q;
>         return qinc;
>     }
> }
>
> void main()
> {
>     B b;
>     assert(b._q == 0);
>     ++b.q; // b.q++ doesn't work, another bug
>     assert(b._q == 1);
> }
>
> But I think actually, if we are going to define get and set in C# style, it would be useful to be able to define all the operators.  So that part of the plan has merit.
>
> I think you need to drop the struct moniker.  This is not a struct.  You do that, and I think your proposal is on more solid ground.
>
>>
>>> Unless, of course, you pass the pointer to myStruct as the 'this' reference.
>>
>> Ain't no "this" in an empty struct. Use "outer.this" instead.
>
> outer is the pointer I am referring to.  It's not magic, it must come from somewhere.  If the struct has no this pointer, it's not a struct.  A data-less struct *STILL* has a this pointer.
>
>>
>>> But then, this isn't a normal struct, and
>>> I'm really failing to see why we have to make this a struct at all.
>>
>> Because it's already implemented, except for a few details, because it opens up possibilities for properties other languages could only dream of, and because it obviates the need for tags like @property to provide far weaker functionality.
>
> Hand waving doesn't solve the problems.  The details are important to resolve.
>
> What it seems like you have done is hijacked the 'struct' keyword for a property.  It isn't a struct, and it doesn't obviate the need for a tag.
>
>> You say it's complicated, but I ask you this: does any other proposal completely eliminate the so-called eye-sore "@property" while also providing functionality with unknown potential which goes far beyond what people are used to? And the owner pointer problem is only a problem if we want to make the language complete and consistent with regard to normal non-static structs holding an outer struct pointer. I think having a use case for this type of struct would seal the deal, but as it is, I'm not sure.
>
> What I mean by complicated is that it seems like a lot more work than it should be.
>
> If we want to define new syntax to define properties, let's do it in a way that is concise and to the point.  I don't want to make properties look like structs, they are not structs.
>
> -Steve

Okay, so you're drawing the line where you think it ends.

I want to point out again that my idea requires the language change which allows struct-nested structs access to their parent's scope. The temporary fix is to make it an error to have a non-static struct-nested struct which actually holds data. All programmers can quickly see what's wrong with their code, because they will have to add "static" to data-containing struct-nested structs to get them to compile. As I said above, this is arguably the behavior which should have been implemented in the first place - I'm actually making the language *more* consistent by insisting on this.

And there is simply no need for a data-less struct to allow a "this" pointer. There will never be any need to know the address of a data-less struct. Any use of it will simply give: "Error: a struct with no data may not contain a 'this' pointer". And any use requiring taking its address is statically disallowed at compile time. This is not a hard feature to implement.

February 01, 2013
On 2013-02-01 15:54:14 +0000, "Zach the Mystic" <reachBUTMINUSTHISzach@gOOGLYmail.com> said:

> And there is simply no need for a data-less struct to allow a "this" pointer. There will never be any need to know the address of a data-less struct. Any use of it will simply give: "Error: a struct with no data may not contain a 'this' pointer". And any use requiring taking its address is statically disallowed at compile time. This is not a hard feature to implement.

I think what Steven is saying is that you're distorting the concept of a struct beyond recognition. What you really want/need is just some kind of namespace inside the outer scope.

Note that D almost has what you want already if you do it through a template mixin:
http://www.digitalmars.com/d/archives/digitalmars/D/properties_using_template_mixins_and_alias_this_87952.html

The 

only thing missing is opGet or an equivalent, and probably a more straightforward syntax. And perhaps someone should check whether the functions can be made virtual too (another requirement that doesn't really belong in a struct).

-- 
Michel Fortin
michel.fortin@michelf.ca
http://michelf.ca/

February 01, 2013
I've had to step away from this for a while, but I want to say that I don't see a problem with the data-less "struct" property idea at all, although calling it a struct is perhaps causing some confusion because real structs tend to host internal data of their own rather than reference it from the outside.

The proposed new type of property does not have to host its own internal data, and its member functions can be called just like the hosts member functions.

The proposed struct property is really something like a namespace for wrapping data and related functions. It is not a regular struct at all, but it does share some of the features of a struct. It like an improved C++ namespace with smarts.

Syntactically, we can allow the property to contain its own data internally, but the data will really belong to the hosted structure (class, struct, or module level). Nothing really new needs to be implemented because there's no special need for storing a "this" pointer, and functions get called in the same way as before. The "this" is the host pointer. We can still refer to an inner and an outter this pointer, but that's really an alias for either the hosts "this" (outter) or the property namespace (inner), both use the same this pointer.

If we view the property more like a namespace, then certain things become clear, for example the property by itself is not a movable structure, it's permanently attached to the host class, struct, or module. We can only take addresses of addressable items if they are visible.

No, the struct-property will not behave exactly like a variable, and I think that idea is a dead end anyway because it's far too complicated to implement and has questionable value. I agree with Walter, it's needs to be put down so we can move on and come up with a better idea that can work and is really useful.

I may intuitively feel this is a great idea, but we will need a few compelling  use cases that solve real world problems to make a case for it.

--rt
February 01, 2013
On Friday, 1 February 2013 at 16:24:53 UTC, Michel Fortin wrote:
> On 2013-02-01 15:54:14 +0000, "Zach the Mystic" <reachBUTMINUSTHISzach@gOOGLYmail.com> said:
>
>> And there is simply no need for a data-less struct to allow a "this" pointer. There will never be any need to know the address of a data-less struct. Any use of it will simply give: "Error: a struct with no data may not contain a 'this' pointer". And any use requiring taking its address is statically disallowed at compile time. This is not a hard feature to implement.
>
> I think what Steven is saying is that you're distorting the concept of a struct beyond recognition.

Yes, this is exactly what I am trying to do, because it's yet another totally badass thing to do. But I'm simply adding it to D's list of such things - CTFE, enums as manifest constants(?), the reuse of keyword static all over the place, strings as immutables. Am I wrong?

> What you really want/need is just some kind of namespace inside the outer scope.
>
> Note that D almost has what you want already if you do it through a template mixin:
> http://www.digitalmars.com/d/archives/digitalmars/D/properties_using_template_mixins_and_alias_this_87952.html
>
> The only thing missing is opGet or an equivalent, and probably a more straightforward syntax. And perhaps someone should check whether the functions can be made virtual too (another requirement that doesn't really belong in a struct).

That's why it's so weird to see people suggesting so many ideas for @property, when this functionality is basically already built into the language. My suggestion already works at module level. Look:

import std.traits;

Front front;
struct Front
{
  ref T opCall(T)(T[] a)
  if (!isNarrowString!(T[]) && !is(T[] == void[]))
  {
      assert(a.length, "Attempting to fetch the front of an empty
array of " ~ typeof(a[0]).stringof);
      return a[0];
  }
}

int main(string[] args)
{

   assert([1,2,3].front == 1);
   return 0;
}

It needs three improvements:
1) allow it to work everywhere, not just module level
2) make it look good with a new friendly syntax, which by the way I'm more proud of than any other suggestions I've made here
3) optimize away the unnecessary hidden pointers
February 01, 2013
On Friday, 1 February 2013 at 17:23:45 UTC, Rob T wrote:
> I've had to step away from this for a while, but I want to say that I don't see a problem with the data-less "struct" property idea at all, although calling it a struct is perhaps causing some confusion because real structs tend to host internal data of their own rather than reference it from the outside.

Hey, thanks for coming back.

>
> The proposed new type of property does not have to host its own internal data, and its member functions can be called just like the hosts member functions.
>
> The proposed struct property is really something like a namespace for wrapping data and related functions. It is not a regular struct at all, but it does share some of the features of a struct. It like an improved C++ namespace with smarts.
>
> Syntactically, we can allow the property to contain its own data internally, but the data will really belong to the hosted structure (class, struct, or module level).

I don't like this because structs don't already work that way. All structs can work in exactly the same way. I'm pretty sure there's no need to treat these specially... except under the hood, where compiler optimizes away the unnecessary pointers in the case of data-free structs.

> Nothing really new needs to be implemented because there's no special need for storing a "this" pointer, and functions get called in the same way as before. The "this" is the host pointer. We can still refer to an inner and an outter this pointer, but that's really an alias for either the hosts "this" (outter) or the property namespace (inner), both use the same this pointer.

The problem with overriding "this" is if you later add data to the struct, it will break any code that already uses "this". "outer" doesn't have this problem at all, and is arguably clearer. Therefore, I think "this" should simply be a compile-time error with a data-free struct.

> If we view the property more like a namespace, then certain things become clear, for example the property by itself is not a movable structure, it's permanently attached to the host class, struct, or module. We can only take addresses of addressable items if they are visible.
>
> No, the struct-property will not behave exactly like a variable, and I think that idea is a dead end anyway because it's far too complicated to implement and has questionable value. I agree with Walter, it's needs to be put down so we can move on and come up with a better idea that can work and is really useful.

I don't know, Rob T... Walter's recent article on half-floats shows that you can get a struct instance to look pretty darn close to a variable. Is there anything a variable can do that half-floats can't do? Because if not, we're in the money, dude. This does exactly the same thing, just with data stored outside the struct itself.

> I may intuitively feel this is a great idea, but we will need a few compelling  use cases that solve real world problems to make a case for it.
>
> --rt

Yes, but on the other hand, if you provide all the functionality previously requested, plus a whole lot more, a compelling use case becomes a lot less important. Undiscovered country.
February 01, 2013
On Fri, 01 Feb 2013 12:27:58 -0500, Zach the Mystic <reachBUTMINUSTHISzach@googlymail.com> wrote:

> On Friday, 1 February 2013 at 16:24:53 UTC, Michel Fortin wrote:
>> I think what Steven is saying is that you're distorting the concept of a struct beyond recognition.
>
> Yes, this is exactly what I am trying to do, because it's yet another totally badass thing to do. But I'm simply adding it to D's list of such things - CTFE, enums as manifest constants(?), the reuse of keyword static all over the place, strings as immutables. Am I wrong?

I think you are wrong in how you assume a struct works, but not in your attempt to implement properties.  Struct is just not a key to this formula.

Note that many (including myself) consider the overloading of static to be a *detriment*.

>
>> What you really want/need is just some kind of namespace inside the outer scope.
>>
>> Note that D almost has what you want already if you do it through a template mixin:
>> http://www.digitalmars.com/d/archives/digitalmars/D/properties_using_template_mixins_and_alias_this_87952.html
>>
>> The only thing missing is opGet or an equivalent, and probably a more straightforward syntax. And perhaps someone should check whether the functions can be made virtual too (another requirement that doesn't really belong in a struct).
>
> That's why it's so weird to see people suggesting so many ideas for @property, when this functionality is basically already built into the language. My suggestion already works at module level. Look:
>
> import std.traits;
>
> Front front;
> struct Front
> {
>    ref T opCall(T)(T[] a)
>    if (!isNarrowString!(T[]) && !is(T[] == void[]))
>    {
>        assert(a.length, "Attempting to fetch the front of an empty
> array of " ~ typeof(a[0]).stringof);
>        return a[0];
>    }
> }
>
> int main(string[] args)
> {
>
>     assert([1,2,3].front == 1);
>     return 0;
> }
>
> It needs three improvements:
> 1) allow it to work everywhere, not just module level
> 2) make it look good with a new friendly syntax, which by the way I'm more proud of than any other suggestions I've made here
> 3) optimize away the unnecessary hidden pointers

You are applying struct where none is needed.

The current front accomplishes what you have shown except it does not require an actual struct instance (as your code does), and it's far easier to understand and implement than your method.

What you are trying to do is establish a *namespace* in which to declare operator overloads.  The whole idea that it has to be a struct is incorrect, it does not need a "struct" wrapper.

I think you are not understanding how a struct is implemented, and how it's methods are implemented.  A struct function without a 'this' reference is a static struct function.  Nothing different from a standard function, except it is in the struct *namespace*.  I think this is what you really are after, a separate *namespace*.

I think *that* idea has merit, and should be examined.  Drop the struct, and you are back to the table.

-Steve