Jump to page: 1 2 3
Thread overview
Returning const? -- A potential solution
Mar 08, 2009
Jason House
Mar 08, 2009
Daniel Keep
Mar 08, 2009
Tim M
Mar 08, 2009
Daniel Keep
Mar 08, 2009
Tim M
Re: Preserving const? -- A potential solution
Mar 08, 2009
Tim M
Mar 08, 2009
Daniel Keep
Mar 08, 2009
Tim M
Mar 08, 2009
Daniel Keep
Mar 08, 2009
Tim M
Mar 08, 2009
Daniel Keep
Re: Virtual templated functions. Previously: Preserving const? -- A potential solution
Mar 08, 2009
Tim M
Mar 08, 2009
Daniel Keep
Mar 08, 2009
Michel Fortin
Mar 08, 2009
Tim M
Mar 08, 2009
Christopher Wright
Mar 08, 2009
Michel Fortin
Mar 08, 2009
Christopher Wright
Mar 08, 2009
Christopher Wright
Mar 08, 2009
Joel C. Salomon
Mar 08, 2009
Jason House
Mar 08, 2009
Daniel Keep
Mar 08, 2009
Denis Koroskin
Mar 08, 2009
Jason House
Mar 08, 2009
Jason House
Mar 09, 2009
Kagamin
Mar 09, 2009
Jason House
March 08, 2009
The ugly const thread got me thinking about the old problem of returning an input while preserving const safety.  I have an idea that seems reasonable...

In a nutshell, I'm thinking that const(T) should be a base type for T,
immutable(T) and return(T).  return(T) is treated in a read-only fashion,
just like const(T) and immutable(T). Here are some of the key things I think
this achieves:
  * Input arguments are never mutated
  * Use of input parameters in calls to functions as const(T) is 100% legal.
  * Temporary variables can legally be defined and used
  * Calling other functions that return return(T) is allowed
  * No code duplication
  * No code bloat (compiler only needs to generate one version of the code)
  * Functions can be virtual

Let's take a relatively simple example: max

return(T) max(return(T) a, return(T) b){ return (a>b)?a:b; }

When max is called, the compiler would examine the inputs for a and b to determine what the true type for return(T) is from the callee's perspective...  So a call with T and immutable(T) would use const(T) as the perceived return type while an argument of T and T would use T as the return type.

At the call site, the compiler would ensure type safety of how the return type is used.  Within max, the compiler would ensure that the arguments are either treated as const(T) in function calls but not mixed with with types T, const(T), or immutable(T)

PS: The return(T) notation is an arbitrary one for the purposes of this post.  We need a technical solution before worrying about the color of the bicycle shed

March 08, 2009

Jason House wrote:
> The ugly const thread got me thinking about the old problem of returning an input while preserving const safety.  I have an idea that seems reasonable...
> 
> In a nutshell, I'm thinking that const(T) should be a base type for T, immutable(T) and return(T).

Wasn't there something like typeof(return) for this?  I do agree that something like this needs to be done: I shouldn't have to muck about with mixins and/or templates just because I've written a function that will work irrespective of const-ness, and thus should work with any const-ness on the arguments.

> return(T) is treated in a read-only fashion,
> just like const(T) and immutable(T). Here are some of the key things I think
> this achieves:
>   * Input arguments are never mutated

>From whose perspective?  This is one of the weird things about
const-ness; it's meaning depends on your perspective.  :P

I assume you meant "Input arguments are never mutated by the callee."

>   * Use of input parameters in calls to functions as const(T) is 100% legal.

Well, one would hope so.  Given that this would mean that arguments
being T, const(T) or immutable(T) would be valid, this seems to be in
the same boat as:

"Features of water: it's got hydrogen in it!"

>   * Temporary variables can legally be defined and used

I don't get this.  In what context?  I wasn't aware that using const(T) in your code prevented you from having temporary variables...

>   * Calling other functions that return return(T) is allowed

I'd bloody well hope so!

>   * No code duplication

THIS right here should be the #1 reason.  Having to create multiple versions of a function just to correctly propogate const-ness is a right PITA.

Yes, templates, mixins, etc.: I shouldn't HAVE to resort to them.

>   * No code bloat (compiler only needs to generate one version of the code)

Perhaps it would be worth adding this:

"Compilers may optionally, as a quality of implementation feature, generate an overload of the function which specifically handles immutable arguments.  This should only be done for optimised builds. Any user code which disallows this optimisation may be reported as a warning."

Considering that one of the reasons for having transitive immutability is to aid in optimisation, it seems a shame to toss it overboard with a pair of lead shoes.

>   * Functions can be virtual

Well, they can be virtual now: you just have to manually instantiate the function 3 times.

> Let's take a relatively simple example: max
> 
> return(T) max(return(T) a, return(T) b){ return (a>b)?a:b; }
> 
> When max is called, the compiler would examine the inputs for a and b to determine what the true type for return(T) is from the callee's perspective...  So a call with T and immutable(T) would use const(T) as the perceived return type while an argument of T and T would use T as the return type.
> 
> At the call site, the compiler would ensure type safety of how the return type is used.  Within max, the compiler would ensure that the arguments are either treated as const(T) in function calls but not mixed with with types T, const(T), or immutable(T)

One would hope the following would work:

static if( isConst!(typeof(a)) )
{
    // Slow, safe way
}
else
{
    // Exploit mutability
}

> PS: The return(T) notation is an arbitrary one for the purposes of this post.  We need a technical solution before worrying about the color of the bicycle shed

It should be red, obviously.  :P

On a more serious note, the return(T) syntax worries me because it looks
like there's a template at play here, but I can't see it.  But as you
say, we should worry about getting Walter to agree this is needed first.  :)


March 08, 2009
On Sun, 08 Mar 2009 17:56:09 +1300, Daniel Keep <daniel.keep.lists@gmail.com> wrote:

>
>
> Jason House wrote:
>> The ugly const thread got me thinking about the old problem of returning an
>> input while preserving const safety.  I have an idea that seems
>> reasonable...
>>
>> In a nutshell, I'm thinking that const(T) should be a base type for T,
>> immutable(T) and return(T).
>
> Wasn't there something like typeof(return) for this?  I do agree that
> something like this needs to be done: I shouldn't have to muck about
> with mixins and/or templates just because I've written a function that
> will work irrespective of const-ness, and thus should work with any
> const-ness on the arguments.
>
>> return(T) is treated in a read-only fashion,
>> just like const(T) and immutable(T). Here are some of the key things I think
>> this achieves:
>>   * Input arguments are never mutated
>
>> From whose perspective?  This is one of the weird things about
> const-ness; it's meaning depends on your perspective.  :P
>
> I assume you meant "Input arguments are never mutated by the callee."
>
>>   * Use of input parameters in calls to functions as const(T) is 100% legal.
>
> Well, one would hope so.  Given that this would mean that arguments
> being T, const(T) or immutable(T) would be valid, this seems to be in
> the same boat as:
>
> "Features of water: it's got hydrogen in it!"
>
>>   * Temporary variables can legally be defined and used
>
> I don't get this.  In what context?  I wasn't aware that using const(T)
> in your code prevented you from having temporary variables...
>
>>   * Calling other functions that return return(T) is allowed
>
> I'd bloody well hope so!
>
>>   * No code duplication
>
> THIS right here should be the #1 reason.  Having to create multiple
> versions of a function just to correctly propogate const-ness is a right
> PITA.
>
> Yes, templates, mixins, etc.: I shouldn't HAVE to resort to them.
>
>>   * No code bloat (compiler only needs to generate one version of the code)
>
> Perhaps it would be worth adding this:
>
> "Compilers may optionally, as a quality of implementation feature,
> generate an overload of the function which specifically handles
> immutable arguments.  This should only be done for optimised builds.
> Any user code which disallows this optimisation may be reported as a
> warning."
>
> Considering that one of the reasons for having transitive immutability
> is to aid in optimisation, it seems a shame to toss it overboard with a
> pair of lead shoes.
>
>>   * Functions can be virtual
>
> Well, they can be virtual now: you just have to manually instantiate the
> function 3 times.
>
>> Let's take a relatively simple example: max
>>
>> return(T) max(return(T) a, return(T) b){ return (a>b)?a:b; }
>>
>> When max is called, the compiler would examine the inputs for a and b to
>> determine what the true type for return(T) is from the callee's
>> perspective...  So a call with T and immutable(T) would use const(T) as the
>> perceived return type while an argument of T and T would use T as the return
>> type.
>>
>> At the call site, the compiler would ensure type safety of how the return
>> type is used.  Within max, the compiler would ensure that the arguments are
>> either treated as const(T) in function calls but not mixed with with types
>> T, const(T), or immutable(T)
>
> One would hope the following would work:
>
> static if( isConst!(typeof(a)) )
> {
>     // Slow, safe way
> }
> else
> {
>     // Exploit mutability
> }
>
>> PS: The return(T) notation is an arbitrary one for the purposes of this
>> post.  We need a technical solution before worrying about the color of the
>> bicycle shed
>
> It should be red, obviously.  :P
>
> On a more serious note, the return(T) syntax worries me because it looks
> like there's a template at play here, but I can't see it.  But as you
> say, we should worry about getting Walter to agree this is needed first.  :)
>
>

What does this mean:

module tconst;

import std.stdio;

invariant(char)[] func()
{
      invariant(char)[] s = "hello";
      return s;
}

void main()
{
      auto s = func();
      s[0] = 'm'; //error
}

I thought we already have returning const/invariant? That code ^ works fine for me.


March 08, 2009
Daniel Keep wrote:

> 
> 
> Jason House wrote:
>> The ugly const thread got me thinking about the old problem of returning
>> an
>> input while preserving const safety.  I have an idea that seems
>> reasonable...
>> 
>> In a nutshell, I'm thinking that const(T) should be a base type for T,
>> immutable(T) and return(T).
> 
> Wasn't there something like typeof(return) for this?  I do agree that something like this needs to be done: I shouldn't have to muck about with mixins and/or templates just because I've written a function that will work irrespective of const-ness, and thus should work with any const-ness on the arguments.
> 
>> return(T) is treated in a read-only fashion,
>> just like const(T) and immutable(T). Here are some of the key things I
>> think this achieves:
>>   * Input arguments are never mutated
> 
>>From whose perspective?  This is one of the weird things about
> const-ness; it's meaning depends on your perspective.  :P
> 
> I assume you meant "Input arguments are never mutated by the callee."

Yes.  The callee never mutates it.



>>   * Use of input parameters in calls to functions as const(T) is 100%
>>   legal.
> 
> Well, one would hope so.  Given that this would mean that arguments
> being T, const(T) or immutable(T) would be valid, this seems to be in
> the same boat as:
> 
> "Features of water: it's got hydrogen in it!"
> 
>>   * Temporary variables can legally be defined and used
> 
> I don't get this.  In what context?  I wasn't aware that using const(T) in your code prevented you from having temporary variables...
> 
>>   * Calling other functions that return return(T) is allowed
> 
> I'd bloody well hope so!
> 
>>   * No code duplication
> 
> THIS right here should be the #1 reason.  Having to create multiple versions of a function just to correctly propogate const-ness is a right PITA.
> 
> Yes, templates, mixins, etc.: I shouldn't HAVE to resort to them.
> 
>>   * No code bloat (compiler only needs to generate one version of the
>>   code)
> 
> Perhaps it would be worth adding this:
> 
> "Compilers may optionally, as a quality of implementation feature, generate an overload of the function which specifically handles immutable arguments.  This should only be done for optimised builds. Any user code which disallows this optimisation may be reported as a warning."
> 
> Considering that one of the reasons for having transitive immutability is to aid in optimisation, it seems a shame to toss it overboard with a pair of lead shoes.

That's an important point that should be addressed after the basic solution is found.


>>   * Functions can be virtual
> 
> Well, they can be virtual now: you just have to manually instantiate the function 3 times.

I was thinking of the contrast to template-based solutions that avoid code duplication, but cause both code bloat and are not virtual.  (Virtual template functions is a whole thread in itself that I hope to avoid at the moment)


>> Let's take a relatively simple example: max
>> 
>> return(T) max(return(T) a, return(T) b){ return (a>b)?a:b; }
>> 
>> When max is called, the compiler would examine the inputs for a and b to
>> determine what the true type for return(T) is from the callee's
>> perspective...  So a call with T and immutable(T) would use const(T) as
>> the perceived return type while an argument of T and T would use T as the
>> return type.
>> 
>> At the call site, the compiler would ensure type safety of how the return
>> type is used.  Within max, the compiler would ensure that the arguments
>> are either treated as const(T) in function calls but not mixed with with
>> types T, const(T), or immutable(T)
> 
> One would hope the following would work:
> 
> static if( isConst!(typeof(a)) )
> {
>     // Slow, safe way
> }
> else
> {
>     // Exploit mutability
> }

In such scenarios, people are not complaining about code duplication and will just code an overloaded function...  I hope that can stay out of this thread since it's really a different problem.


>> PS: The return(T) notation is an arbitrary one for the purposes of this post.  We need a technical solution before worrying about the color of the bicycle shed
> 
> It should be red, obviously.  :P
> 
> On a more serious note, the return(T) syntax worries me because it looks like there's a template at play here, but I can't see it.  But as you say, we should worry about getting Walter to agree this is needed first. :)


March 08, 2009

Jason House wrote:
> Daniel Keep wrote:
>> Perhaps it would be worth adding this:
>>
>> "Compilers may optionally, as a quality of implementation feature, generate an overload of the function which specifically handles immutable arguments.  This should only be done for optimised builds. Any user code which disallows this optimisation may be reported as a warning."
>>
>> Considering that one of the reasons for having transitive immutability is to aid in optimisation, it seems a shame to toss it overboard with a pair of lead shoes.
> 
> That's an important point that should be addressed after the basic solution is found.

Why?  If the solution is designed without thought to this, you could end up in a situation where it's no longer possible.

I'm not saying this has to be done from the get-go; I'm saying it should be kept in mind so we don't lock ourselves out from it.

>>>   * Functions can be virtual
>> Well, they can be virtual now: you just have to manually instantiate the function 3 times.
> 
> I was thinking of the contrast to template-based solutions that avoid code duplication, but cause both code bloat and are not virtual.  (Virtual template functions is a whole thread in itself that I hope to avoid at the moment)

You can define a templated function, then mix it in with three different type arguments.  Yes, there's two functions in there which probably have the same implementation, but they're still virtual.

As for virtual templates, that's impossible unless the D runtime grows a compiler.

>> One would hope the following would work:
>>
>> static if( isConst!(typeof(a)) )
>> {
>>     // Slow, safe way
>> }
>> else
>> {
>>     // Exploit mutability
>> }
> 
> In such scenarios, people are not complaining about code duplication and will just code an overloaded function...  I hope that can stay out of this thread since it's really a different problem.

What if it's a one-line difference in a 20-line function?

And it's very related to this: you either allow the above construct or you don't.

Let's say the compiler takes the method, generates the single implementation, and off it goes.

How does it parse the above construct?  You've got to define THAT either way.

  -- Daniel
March 08, 2009
If you're not actually responding to a post, please don't quote the entire thing in your message.

Tim M wrote:
> What does this mean:
> 
> module tconst;
> 
> import std.stdio;
> 
> invariant(char)[] func()
> {
>       invariant(char)[] s = "hello";
>       return s;
> }
> 
> void main()
> {
>       auto s = func();
>       s[0] = 'm'; //error
> }
> 
> I thought we already have returning const/invariant? That code ^ works fine for me.

You missed the point.  This has nothing to do with returning invariant types.  Jason is proposing a way to create a function which maintains the const-ness of its arguments without having to implement multiple versions.  In other words,

return(T) max(return(T) a, return(T) b){ return (a>b)?a:b; }

Would be similar to the following:

T max(T a, T b){ return (a>b)?a:b; }
const(T) max(const(T) a, const(T) b){ return (a>b)?a:b; }
invariant(T) max(invariant(T) a, invariant(T) b){ return (a>b)?a:b; }

Except that each would share a single implementation.

  -- Daniel
March 08, 2009
On Sun, 08 Mar 2009 19:18:52 +1300, Daniel Keep <daniel.keep.lists@gmail.com> wrote:

>
> If you're not actually responding to a post, please don't quote the
> entire thing in your message.
>
> Tim M wrote:
>> What does this mean:
>>
>> module tconst;
>>
>> import std.stdio;
>>
>> invariant(char)[] func()
>> {
>>       invariant(char)[] s = "hello";
>>       return s;
>> }
>>
>> void main()
>> {
>>       auto s = func();
>>       s[0] = 'm'; //error
>> }
>>
>> I thought we already have returning const/invariant? That code ^ works
>> fine for me.
>
> You missed the point.  This has nothing to do with returning invariant
> types.  Jason is proposing a way to create a function which maintains
> the const-ness of its arguments without having to implement multiple
> versions.  In other words,
>
> return(T) max(return(T) a, return(T) b){ return (a>b)?a:b; }
>
> Would be similar to the following:
>
> T max(T a, T b){ return (a>b)?a:b; }
> const(T) max(const(T) a, const(T) b){ return (a>b)?a:b; }
> invariant(T) max(invariant(T) a, invariant(T) b){ return (a>b)?a:b; }
>
> Except that each would share a single implementation.
>
>   -- Daniel

March 08, 2009
>> If you're not actually responding to a post, please don't quote the
>> entire thing in your message.

The subject should have been more accurate then but yes preserving constness is very usefull though I prefer the inferred template way:

module tconst;

import std.stdio;

T max(T)(T a, T b)
{
      if(a > b)
            return a;
      else
            return b;
}

void main()
{
      invariant int a = 2;
      invariant int b = 4;
      auto c = max(a,b); //c becomes an invariant int
      c = 5; //should fail as c is also invariant
}
March 08, 2009

Tim M wrote:
> 
>>> If you're not actually responding to a post, please don't quote the entire thing in your message.
> 
> The subject should have been more accurate then but yes preserving constness is very usefull though I prefer the inferred template way:

One issue with this is that template functions can't be virtual.

You can work around it, but it's really putting barriers up to easy use of the new const system, which I think is a bad thing.

  -- Daniel
March 08, 2009
On Sun, 08 Mar 2009 20:12:20 +1300, Daniel Keep <daniel.keep.lists@gmail.com> wrote:

>
> One issue with this is that template functions can't be virtual.
>
> You can work around it, but it's really putting barriers up to easy use
> of the new const system, which I think is a bad thing.
>
>   -- Daniel

I'd rather have virtual template functions, even if it means I have to re-compile all subclasses when the vtbls get modified. Is there any technicality causing it to be impossible to implement or just something rather tricky?
« First   ‹ Prev
1 2 3