write once type?

Steven Schveighoffer schveiguy at gmail.com
Fri Apr 30 13:36:10 UTC 2021


On 4/30/21 9:24 AM, sighoya wrote:
> On Friday, 30 April 2021 at 01:30:54 UTC, Steven Schveighoffer wrote:
> 
>> In my case, for value of a certain type in the loop, I was storing a 
>> specific field from the first one I found, and then verifying that all 
>> the other values of that type (not exactly the same type, but similar) 
>> had the same value for that field, otherwise it was an error.
>>
>> I could have done it in 2 loops, but it's wasteful.
> 
> I have problems to grok exactly what you're talking about, can you 
> provide some neat and small example for this?
> 
> 

I'll just show you the code here. This is from my (up and coming) sql 
builder project.

A `ColumnDef!T` is an sql table column of type `T`, which contains an 
expression describing the column, and a table definition of where the 
column comes from. `TableDef` is const inside the column def:

```d
struct ColumnDef(T)
{
     const TableDef table;
     ExprString expr;
     alias type = T;
}
```

Now, what if you wanted a "computed" column? that is, you wanted to 
define a column with a type, but that is computed from other columns? 
Like `select total - tax as netcost from sometable`

What I want to create is something that returns a ColumnDef from an 
arbitrary expression. But there is only one table definition, so if you 
want to return a ColumnDef, you need to ensure there is only one source 
table. So here is my code that does that:

```d
ColumnDef!T exprCol(T, Args...)(Args args)
{
     // first, find all columns, and ensure that table defs are all from the
     // same table (a ColumnDef cannot have multiple tables).
     const(TableDef)* tabledef;
     foreach(ref arg; args)
     {
         static if(is(typeof(arg) == ColumnDef!U, U))
         {
             if(tabledef && arg.table != *tabledef)
                 throw new Exception("can't have multiple tabledefs in 
the expression");
             else
                 tabledef = &arg.table;
         }
     }

     assert(tabledef !is null);

     // build the expr string
     ExprString expr;
     foreach(ref a; args)
     {
         static if(is(typeof(a) == string))
             expr ~= a;
         else
             expr ~= a.expr;
     }
     return ColumnDef!(T)(*tabledef, expr);
}
```

The pointer abstraction works perfectly, but if there was no way to use 
that abstraction, there aren't any D facilities to allow this. It's 
really tail-const that I need.

I could have looped 2x over the args, and used an inner function to 
fetch the first one (or maybe used a staticIndexOf to get the first 
ColumnDef thing), and a second loop to verify all the remaining args 
have the same table def, but this loop works just as designed and is 
super-readable.

-Steve


More information about the Digitalmars-d-learn mailing list