www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - write once type?

reply Steven Schveighoffer <schveiguy gmail.com> writes:
I have had the need in some cases to *maybe* set a const value inside a 
loop. One can sometimes abstract this into a lambda function, but 
sometimes this is not possible (e.g. if the loop is static). Not only 
that, but I may also want to keep processing the loop and do something 
different if the value has already been set instead of returning 
immediately, which necessitates a second loop.

My use case is I have a property that is const inside a set of types, 
and I want to verify that they all have the same value, and extract what 
that value is.

I'm wondering if anyone has a "Write once" type, that is, a type that 
allows you to write it exactly once, and is treated like initialization 
on first setting (i.e. allows writing to previously unused const data).

This type has to decide at runtime whether it has been set. So it would 
kind of be like Nullable!T, but Nullable doesn't allow setting when the 
T is const. Optional!T doesn't work either.

-Steve
Apr 20
next sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 4/20/21 3:56 PM, Steven Schveighoffer wrote:
 I have had the need in some cases to *maybe* set a const value inside a 
 loop. One can sometimes abstract this into a lambda function, but 
 sometimes this is not possible (e.g. if the loop is static). Not only 
 that, but I may also want to keep processing the loop and do something 
 different if the value has already been set instead of returning 
 immediately, which necessitates a second loop.
 
 My use case is I have a property that is const inside a set of types, 
 and I want to verify that they all have the same value, and extract what 
 that value is.
 
 I'm wondering if anyone has a "Write once" type, that is, a type that 
 allows you to write it exactly once, and is treated like initialization 
 on first setting (i.e. allows writing to previously unused const data).
 
 This type has to decide at runtime whether it has been set. So it would 
 kind of be like Nullable!T, but Nullable doesn't allow setting when the 
 T is const. Optional!T doesn't work either.
I just realized, this is Rebindable, or tail-const. (I don't need the head to be truly const, I just need to be able to copy into a const-referring thing). I currently am using a pointer, which is working, but I prefer not to use it, and it is not general enough. -Steve
Apr 20
parent ichneumwn <idonotenjoyemail idonotenjoyemail.org> writes:
On Tuesday, 20 April 2021 at 20:04:17 UTC, Steven Schveighoffer 
wrote:
 I just realized, this is Rebindable, or tail-const. (I don't 
 need the head to be truly const, I just need to be able to copy 
 into a const-referring thing). I currently am using a pointer, 
 which is working, but I prefer not to use it, and it is not 
 general enough.

 -Steve
Ah, you found it - I was looking up the reference for it and you replied in the mean-time. Ignore my answer :)
Apr 20
prev sibling next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Tue, Apr 20, 2021 at 03:56:33PM -0400, Steven Schveighoffer via
Digitalmars-d-learn wrote:
[...]
 I'm wondering if anyone has a "Write once" type, that is, a type that
 allows you to write it exactly once, and is treated like
 initialization on first setting (i.e. allows writing to previously
 unused const data).
Maybe a const class? The reference is null until you initialize it by allocating a new object and initializing it in the ctor. But sounds like you want a value type instead. Technically, allocating a const class involves the GC assigning some region of memory to the class, initializing it, then casting it to const. So I'd imagine that the by-value equivalent would require a const cast somewhere, probably in a trusted block if you want it to work with safe. Which means that probably you'll need a trusted cast somewhere in your implementation. So perhaps something like this: struct WriteOnce(T) { const T payload; const bool isSet; void opAssign(U : T)(U data) in (!isSet) { assert(!isSet); trusted() { *(cast()&payload) = data; *(cast()&isSet) = true; }(); } } T -- Prosperity breeds contempt, and poverty breeds consent. -- Suck.com
Apr 20
prev sibling next sibling parent ichneumwn <idonotenjoyemail idonotenjoyemail.org> writes:
On Tuesday, 20 April 2021 at 19:56:33 UTC, Steven Schveighoffer 
wrote:
 I have had the need in some cases to *maybe* set a const value 
 inside a loop. One can sometimes abstract this into a lambda 
 function, but sometimes this is not possible (e.g. if the loop 
 is static). Not only that, but I may also want to keep 
 processing the loop and do something different if the value has 
 already been set instead of returning immediately, which 
 necessitates a second loop.

 My use case is I have a property that is const inside a set of 
 types, and I want to verify that they all have the same value, 
 and extract what that value is.

 I'm wondering if anyone has a "Write once" type, that is, a 
 type that allows you to write it exactly once, and is treated 
 like initialization on first setting (i.e. allows writing to 
 previously unused const data).

 This type has to decide at runtime whether it has been set. So 
 it would kind of be like Nullable!T, but Nullable doesn't allow 
 setting when the T is const. Optional!T doesn't work either.

 -Steve
Not quite your use case, I think, but std.typecons.Rebindable at least allows you to bind a new const(object). Not write once though. I think I found that after a hint of its existence in https://forum.dlang.org/thread/orpbvvjspibfpitnnpxd forum.dlang.org
Apr 20
prev sibling next sibling parent Jack <jckj33 gmail.com> writes:
On Tuesday, 20 April 2021 at 19:56:33 UTC, Steven Schveighoffer 
wrote:
 I have had the need in some cases to *maybe* set a const value 
 inside a loop. One can sometimes abstract this into a lambda 
 function, but sometimes this is not possible (e.g. if the loop 
 is static). Not only that, but I may also want to keep 
 processing the loop and do something different if the value has 
 already been set instead of returning immediately, which 
 necessitates a second loop.

 [...]
I've tried to mimic it with class/struct (including readonly with C++) but it isn't same thing.
Apr 25
prev sibling parent reply sighoya <sighoya gmail.com> writes:
On Tuesday, 20 April 2021 at 19:56:33 UTC, Steven Schveighoffer 
wrote:
 Not only that, but I may also want to keep processing the loop 
 and do something different if the value has already been set 
 instead of returning immediately, which necessitates a second 
 loop.
Can I ask why you require to handle it in one loop? As I see it, you are required to poll over isSet/isNotSet in every iteration.
Apr 29
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 4/29/21 5:31 PM, sighoya wrote:
 On Tuesday, 20 April 2021 at 19:56:33 UTC, Steven Schveighoffer wrote:
 Not only that, but I may also want to keep processing the loop and do 
 something different if the value has already been set instead of 
 returning immediately, which necessitates a second loop.
Can I ask why you require to handle it in one loop? As I see it, you are required to poll over isSet/isNotSet in every iteration.
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. -Steve
Apr 29
parent reply sighoya <sighoya gmail.com> writes:
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?
Apr 30
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
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
Apr 30
parent sighoya <sighoya gmail.com> writes:
On Friday, 30 April 2021 at 13:36:10 UTC, Steven Schveighoffer 
wrote:
 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.
I would say likewise using static indexing, is it really that slow? Otherwise, iterate over Pair-like partitions would also be a solution and compare if first.tableDef is second.tableDef, don't know if D has something for this statically available. Further, I would like to refactor the static if out to an in-requirement, though I'm unsure if in-assertions support arbitrary exception types.
Apr 30