Two cases showing imperfection of the const system

Timon Gehr timon.gehr at
Thu Feb 16 14:42:17 PST 2012

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
> ).
> 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:

> 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!)

More information about the Digitalmars-d mailing list