View mode: basic / threaded / horizontal-split · Log in · Help
February 16, 2012
Two cases showing imperfection of the const system
Firstly, let me preface this... if you use templates to get 
around the const system's imperfections, you are admitting that 
the const system is broken. Now, on with the program.

My unique experience in using D2 without Phobos lead me to 
encounter two cases that show how the D2 const system is just a 
pain in the behind for some really reasonable tasks.

First case:

You want to sort an array of strings using a function. Strings 
can be all of these types: char[], const(char)[] and 
immutable(char)[]. What would be the signature of such a function?

It can't be sort(const(char)[][]) because it's unsafe to case 
char[][] and immutable(char)[][] to that argument type (see 
http://d.puremagic.com/issues/show_bug.cgi?id=4251 ).
It can't be sort(const(char[])[]) because you can't actually sort 
that!

The only good way I found is to use a cast inside the function 
with the second signature. Obviously I'm glad there's a 
workabout, but surely a cast isn't a good thing.

Second case:

inout was meant to solve issues with functions that return slices 
of inputs. What about a class that is dedicated to the same 
functionality?

E.g. this works fine:

inout(char)[] half(inout(char)[]);

But what about this:

struct Slicer
{
  char[] a;
  char[] half();
}

Note that the type of the input (the member 'a') must be the same 
as the output of the half method. I don't know how to accomplish 
this without templates. But as I said in the preface, you 
shouldn't need templates for such a simple task. Note that doing 
this isn't satisfactory:

struct Slicer
{
  char[] a;
  inout(char)[] half() inout;
}

because there may be other members inside that struct that may 
need to remain mutable.

This is very relevant, incidentally, for the planned library 
implementation of associative arrays. How would this be 
implemented when an associative array is a struct?

inout(char)[] test(inout(char)[])
{
  inout(char)[][int] a;
}

It doesn't even compile now (in dmd 2.058).

I don't have any solutions to these problems, incidentally... I 
think they are complex, but definitely worthy of having a 
reasonable solution that doesn't involve needless (in this case) 
templates.

-SiegeLord
February 16, 2012
Re: Two cases showing imperfection of the const system
On 02/16/2012 10:48 PM, SiegeLord wrote:
> Firstly, let me preface this... if you use templates to get around the
> const system's imperfections, you are admitting that the const system is
> broken. Now, on with the program.
>
> My unique experience in using D2 without Phobos lead me to encounter two
> cases that show how the D2 const system is just a pain in the behind for
> some really reasonable tasks.
>
> First case:
>
> You want to sort an array of strings using a function. Strings can be
> all of these types: char[], const(char)[] and immutable(char)[]. What
> would be the signature of such a function?
>
> It can't be sort(const(char)[][]) because it's unsafe to case char[][]
> and immutable(char)[][] to that argument type (see
> http://d.puremagic.com/issues/show_bug.cgi?id=4251 ).
> It can't be sort(const(char[])[]) because you can't actually sort that!
>

The correct signature is
void sort(inout(char)[][]);

This is currently illegal, see:
http://d.puremagic.com/issues/show_bug.cgi?id=7105



> The only good way I found is to use a cast inside the function with the
> second signature. Obviously I'm glad there's a workabout, but surely a
> cast isn't a good thing.
>

A type-safe workaround is to use the signature
inout(void) sort(inout(char)[][]);

> Second case:
>
> inout was meant to solve issues with functions that return slices of
> inputs. What about a class that is dedicated to the same functionality?
>
> E.g. this works fine:
>
> inout(char)[] half(inout(char)[]);
>
> But what about this:
>
> struct Slicer
> {
> char[] a;
> char[] half();
> }
>
> Note that the type of the input (the member 'a') must be the same as the
> output of the half method. I don't know how to accomplish this without
> templates. But as I said in the preface, you shouldn't need templates
> for such a simple task. Note that doing this isn't satisfactory:
>
> struct Slicer
> {
> char[] a;
> inout(char)[] half() inout;
> }
>
> because there may be other members inside that struct that may need to
> remain mutable.
>

For this example, there is no problem. But I see what you mean. Seems 
like it would require some kind of parametric polymorphism. Ideally we'd 
get Scala-like Generics with Java Wildcards ;)

struct Slicer[+T <: const(char)[]]{ // not a template!
    T a;
    int[] someOtherMember;
    inout(T) half() inout;
}

void main(){
    Slicer![char] sl1;
    sl1.a = "string".dup;
    char[] arr1 = sl1.half();
    Slicer![immutable(char)] sl2;
    sl2.a = "string";
    string arr2 = sl2.half();
    Slicer![const(char)] sl3;
    sl3.a = "string".dup;
    sl3.a = "string";
    const(char)[] arr3 = sl3.half();

    sl3 = sl1;
    sl3 = sl2;
}


> This is very relevant, incidentally, for the planned library
> implementation of associative arrays. How would this be implemented when
> an associative array is a struct?
>
> inout(char)[] test(inout(char)[])
> {
> inout(char)[][int] a;
> }
>
> It doesn't even compile now (in dmd 2.058).
>

It does. The problem is that your function is missing a return 
statement. Anyway, when an associative array is a template struct then 
there is basically no non-magical way to implement what you want. 
However, if we introduce generics, the solution would look similar to 
this sketch:

struct AssocArray(S, T)[K <: const(S), V <: const(T)]{
    V lookup(K);
    ...
}

'test' would be rewritten by the compiler as:

inout(char)[] test(inout(char)[]) {
    AssocArray!(int,char[])![int,inout(char)[]] a;
    // ...
}


> I don't have any solutions to these problems, incidentally... I think
> they are complex, but definitely worthy of having a reasonable solution
> that doesn't involve needless (in this case) templates.
>
> -SiegeLord

The first problem is trivial, solving the second one in a type safe way 
would require adding parametric polymorphism to D. (Which I'd love to have!)
February 16, 2012
Re: Two cases showing imperfection of the const system
> The first problem is trivial, solving the second one in a type safe way
> would require adding parametric polymorphism to D. (Which I'd love to have!)
>

Can't you emulate type-safe parametric polymorphism with template constraints?
February 16, 2012
Re: Two cases showing imperfection of the const system
On 2/16/12 3:48 PM, SiegeLord wrote:
> Firstly, let me preface this... if you use templates to get around the
> const system's imperfections, you are admitting that the const system is
> broken. Now, on with the program.

Hold them horses. I disagree. You're just saying it, but what's your basis?

> My unique experience in using D2 without Phobos lead me to encounter two
> cases that show how the D2 const system is just a pain in the behind for
> some really reasonable tasks.
>
> First case:
>
> You want to sort an array of strings using a function. Strings can be
> all of these types: char[], const(char)[] and immutable(char)[]. What
> would be the signature of such a function?

This boils down to: "You want to sort an array of T[], U[], or V[], 
where the three types are loosely-related, except U is a supertype of 
both T and V and the three have the same layout. What would be the 
signature of such a function?"

The answer is (to a reasonable approximation) simple:

sort(X)(X[] data) if (is(X : U) && X.sizeof == U.sizeof);

This has nothing to do with qualifiers. Qualified types are distinct, 
and obey the classic subtyping and layout rules known since the dawn of 
humankind: const is a supertype of mutable and immutable, and they all 
have the same layout. Complaining about that equates to complaining 
about subtyping.

> It can't be sort(const(char)[][]) because it's unsafe to case char[][]
> and immutable(char)[][] to that argument type (see
> http://d.puremagic.com/issues/show_bug.cgi?id=4251 ).
> It can't be sort(const(char[])[]) because you can't actually sort that!
>
> The only good way I found is to use a cast inside the function with the
> second signature. Obviously I'm glad there's a workabout, but surely a
> cast isn't a good thing.

You'd do the same if you wanted to sort arrays of base and arrays of 
derived with the same routine.

> Second case:
>
> inout was meant to solve issues with functions that return slices of
> inputs. What about a class that is dedicated to the same functionality?
>
> E.g. this works fine:
>
> inout(char)[] half(inout(char)[]);
>
> But what about this:
>
> struct Slicer
> {
> char[] a;
> char[] half();
> }
>
> Note that the type of the input (the member 'a') must be the same as the
> output of the half method. I don't know how to accomplish this without
> templates.

I don't know how to swim with a hand tied to my back, either. The 
correct approach is to integrate templates in the discussion and analyze 
_that_ context, not the artificial context that precludes templates. D 
is not Go.

> But as I said in the preface, you shouldn't need templates
> for such a simple task.

char and const char are different types. The embedded presupposition is 
that they are somewhat similar, the qualifier being some sort of 
attribute of the type. That's not the case.

> Note that doing this isn't satisfactory:
>
> struct Slicer
> {
> char[] a;
> inout(char)[] half() inout;
> }
>
> because there may be other members inside that struct that may need to
> remain mutable.

Agreed.

> This is very relevant, incidentally, for the planned library
> implementation of associative arrays. How would this be implemented when
> an associative array is a struct?
>
> inout(char)[] test(inout(char)[])
> {
> inout(char)[][int] a;
> }
>
> It doesn't even compile now (in dmd 2.058).

Associative arrays must be templates.

> I don't have any solutions to these problems, incidentally... I think
> they are complex, but definitely worthy of having a reasonable solution
> that doesn't involve needless (in this case) templates.

Again, we must make templates a part of the setup and discuss what's 
going on.


Andrei
February 16, 2012
Re: Two cases showing imperfection of the const system
On 02/17/2012 12:08 AM, James Miller wrote:
>> The first problem is trivial, solving the second one in a type safe way
>> would require adding parametric polymorphism to D. (Which I'd love to have!)
>>
>
> Can't you emulate type-safe parametric polymorphism with template constraints?

In general, no.

class A{
    T foo[T](T x){ ... }
}
class B{
    override T foo[T](T x){ ... }
}

Often you can, but then you get unnecessary code duplication, which is 
presumably why SiegeLord dislikes templates.

T test(T)(T x) if(is(T:const(char[])){
    T[int] a;
}
February 16, 2012
Re: Two cases showing imperfection of the const system
On 02/17/2012 12:36 AM, Timon Gehr wrote:
> On 02/17/2012 12:08 AM, James Miller wrote:
>>> The first problem is trivial, solving the second one in a type safe way
>>> would require adding parametric polymorphism to D. (Which I'd love to
>>> have!)
>>>
>>
>> Can't you emulate type-safe parametric polymorphism with template
>> constraints?
>
> In general, no.
>
> class A{
> T foo[T](T x){ ... }
> }
> class B{
> override T foo[T](T x){ ... }
> }
>
> Often you can, but then you get unnecessary code duplication, which is
> presumably why SiegeLord dislikes templates.
>
> T test(T)(T x) if(is(T:const(char[])){
> T[int] a;
> }

This is closer to the wanted semantics:
T[] test(T)(T[] x) if(is(T[]:const(char[])){
    T[][int] a;
}
February 17, 2012
Re: Two cases showing imperfection of the const system
> It does. The problem is that your function is missing a return 
> statement.

Try it with the return statement, it doesn't compile anyway.

-SiegeLord
February 17, 2012
Re: Two cases showing imperfection of the const system
On 02/17/2012 01:19 AM, SiegeLord wrote:
>> It does. The problem is that your function is missing a return statement.
>
> Try it with the return statement, it doesn't compile anyway.
>
> -SiegeLord
>
>

This compiles with DMD 2.057 and DMD 2.058:

import std.stdio;
inout(char)[] test(inout(char)[] x){
    inout(char)[][int] a;
    a[1]=x;
    return a[1];
}

void main(){
    writeln(test(['a']));
}
February 17, 2012
Re: Two cases showing imperfection of the const system
On Thursday, 16 February 2012 at 23:14:54 UTC, Andrei 
Alexandrescu wrote:
> Hold them horses. I disagree. You're just saying it, but what's 
> your basis?

Because some cases (as shown below) trivially work within the 
const system, while some closely related ones don't. You're not 
going to be able to convince me of a demarcation that requires 
some const issues to require templates, and some not.

> This boils down to: "You want to sort an array of T[], U[], or 
> V[], where the three types are loosely-related, except U is a 
> supertype of both T and V and the three have the same layout. 
> What would be the signature of such a function?"
>
> The answer is (to a reasonable approximation) simple:
>
> sort(X)(X[] data) if (is(X : U) && X.sizeof == U.sizeof);
>
> This has nothing to do with qualifiers.

Because you removed them. While I agree with the type argument to 
a point, qualifiers have more meaning than just arbitrary type 
creation: they talk about mutability. The desired function 
signature states that the contents of the strings that are in the 
array will not be modified, your generic version does not have 
that stipulation. I can make up a body for that sort function 
which will work for types which fit your description, but fail 
for const(char)[], char[] and immutable(char)[].

>
>> Second case:
>>
>> inout was meant to solve issues with functions that return 
>> slices of
>> inputs. What about a class that is dedicated to the same 
>> functionality?
>>
>> E.g. this works fine:
>>
>> inout(char)[] half(inout(char)[]);
>>
>> But what about this:
>>
>> struct Slicer
>> {
>> char[] a;
>> char[] half();
>> }
>>
>> Note that the type of the input (the member 'a') must be the 
>> same as the
>> output of the half method. I don't know how to accomplish this 
>> without
>> templates.
>
> I don't know how to swim with a hand tied to my back, either. 
> The correct approach is to integrate templates in the 
> discussion and analyze _that_ context, not the artificial 
> context that precludes templates. D is not Go.

As much as you might prefer D to be 100% about templates, it is 
not, and there is a subset of it which is usable without them. 
This subset is the subject of this thread. There is no a priori 
reason why the first case should work and second should not 
within the confines of the const system.

> Associative arrays must be templates.

That's fine, but please try to compile this:

struct AAType(T)
{
  T[] Elems;
}

inout(char)[] test(inout(char)[] a)
{
   AAType!(inout(char)[]) b;
   return a;
}

-SiegeLord
February 17, 2012
Re: Two cases showing imperfection of the const system
On Friday, 17 February 2012 at 00:34:41 UTC, Timon Gehr wrote:
> This compiles with DMD 2.057 and DMD 2.058:
>
> import std.stdio;
> inout(char)[] test(inout(char)[] x){
>    inout(char)[][int] a;
>    a[1]=x;
>    return a[1];
> }
>
> void main(){
>    writeln(test(['a']));
> }

Whoops, nevermind then on it not working now. I look forward to 
seeing how it will work when AA's are templates.

-SiegeLord
« First   ‹ Prev
1 2
Top | Discussion index | About this forum | D home