An Optional!T and the implementation of the underlying type's opUnary

aliak something at something.com
Wed Jul 25 12:51:08 UTC 2018


Hi, I'm working on an Optional!T type that is a mixture of 
Scala's Option[T] (i.e. range based) and Swift's and Kotlin's T? 
(i.e. safe dispatching). I'm interested in hearing about 
mutability concerns.

So I want operations on the optional to dispatch to the 
underlying type T if it's present. So let's take opUnary as an 
example, this is how it's currently implemented:

     auto opUnary(string op)() {
         return this.opUnaryImpl!op();
     }
     auto opUnary(string op)() const {
         return this.opUnaryImpl!(op, const(T))();
     }
     auto opUnary(string op)() immutable {
         return this.opUnaryImpl!(op, immutable(T))();
     }
     private auto opUnaryImpl(string op, U = T)() const {
         import std.traits: isPointer;
         static if (op == "*" && isPointer!U) {
             import std.traits: PointerTarget;
             alias P = PointerTarget!U;
             return empty || front is null ? no!P : 
some(cast(P)*this._value);
         } else {
             if (empty) {
                 return no!U;
             } else {
                 return some(mixin(op ~ "cast(U)_value"));
             }
         }
     }

(functions "some" and "no" are type constructors which return an 
Optional!T of whatever the argument type is - except "no" needs 
an explicit T argument)

Why not "opUnary(string op)() inout"?

The reason it's like this is because I want to transfer the 
constness of "this" to the value T that is stored inside. If I 
rewrite "opUnaryImpl()() const" as "opUnary()() inout" and remove 
the implementation for mutable, const, and immutable, then this 
works:

immutable a = Optional!int(3);
++a;

And the internal value is modified.

Should that be allowed?

The caveat is that 1) I want Optional!T to be nogc compatible. So 
therefore the value is stored similarly to this PR in phobos [1] 
(also for an Optional type)

And 2) Optional!T provides an "unwrap" function that returns a T 
(if T is a class or interface), or a T*. So, if I allow 
modification by using inout on opUnary, then for the sake of 
consistency, I should be able to do this:

immutable a = Optional!int(3);
a = 4;

But I can't do this because Optional.opAssign would be either 
inout or immutable and I can't modify this.value = newValue;

And then what about:

auto a = Optional(immutable int)(3);
a = 3; // should this be allowed?

If it is allowed then this will fail because of the nogc 
requirement:

unittest {
     Optional!(immutable int) a = some!(immutable int)(5);
     immutable(int)* p = a.unwrap;
     assert(*p == 5);
     a = 4;
     assert(*a.unwrap == 4);
     assert(*p == 5);
}

Comments, suggestions, opinions?

Cheers,
- Ali

[1] https://github.com/dlang/phobos/pull/3915


More information about the Digitalmars-d mailing list