Thread overview
[Issue 17896] Alternate version of std.conv.to which returns Nullable
October 12
https://issues.dlang.org/show_bug.cgi?id=17896

Steven Schveighoffer <schveiguy@yahoo.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |schveiguy@yahoo.com

--- Comment #1 from Steven Schveighoffer <schveiguy@yahoo.com> ---
Can't we just hook with:

to!(Nullable!int)

?

--
October 12
https://issues.dlang.org/show_bug.cgi?id=17896

--- Comment #2 from Jonathan M Davis <issues.dlang@jmdavisProg.com> ---
(In reply to Steven Schveighoffer from comment #1)
> Can't we just hook with:
> 
> to!(Nullable!int)
> 
> ?

Maybe. It's not something I thought of. However, it _is_ possible right now to have opCast return a Nullable!int for a user-defined type, in which case, making to!(Nullable!int) special probably would not play well with that. Also, I'd expect something like to!(Nullable!int)(42) to be essentially equivalent to nullable(42), which I guess that it still ultimately would be, but you do start to run the risk of having problems with any conversions right now that would use Nullable and work.

It might actually ultimately work quite well, but it does seem to me like it would be clearer and less risky to create a new function than to try and special-case to - especially with how complicated it already is. It wouldn't be hard for a corner case to cause problems that just wouldn't exist with a separate function.

--
October 13
https://issues.dlang.org/show_bug.cgi?id=17896

--- Comment #3 from Steven Schveighoffer <schveiguy@yahoo.com> ---
Anything that uses opCast is circumventing to's builtin mechanisms anyway as to defers to the type "It knows better than me". It's no different here.

struct S1
{
   string s;
   auto opCast(T : Nullable!int)() { return customizedConversion(); }
}

struct S2
{
   string s;
   auto opCast(T : int)() { return customizedConversion(); } // can throw
}

void main()
{
   S1 s1;
   S2 s2;
   auto x = s1.to!(Nullable!int); // s1.opCast!(Nullable!int)
   auto y = s2.to!(Nullable!int); // Nullable!int _tmp; try { _tmp =
s2.opCast!int; } catch {}; y = _tmp;
}

And of course, normal builtins cast to Nullable!T should not do a throw and catch, but optimize that out.

--
October 13
https://issues.dlang.org/show_bug.cgi?id=17896

--- Comment #4 from Jonathan M Davis <issues.dlang@jmdavisProg.com> ---
(In reply to Steven Schveighoffer from comment #3)
> Anything that uses opCast is circumventing to's builtin mechanisms anyway as to defers to the type "It knows better than me". It's no different here.

As far as std.conv.to goes, opCast is just a way for the type to codify a conversion to another type - the same as constructors except that that defines how to convert to the type instead of from it.

But regardless, we're talking about adding a way to attempt a conversion and have it return Nullable if the conversion fails, and if the type casts to Nullable!int, it likely means something else other than the cast attempted to convert to int and failed. We have no idea what the person who created that opCast meant without looking at its documentation. It could have entirely different semantics.

I really think that it's asking for trouble to try and make std.conv.to do double-duty here and conflate converting to Nullable!T in general as being an attempt to convert to T that failed as opposed to having a specific function that's explicitly designed to return Nullable where null indicates a failure instead of using an exception.

I also think that special-casing Nullable!T for to will make it more confusing for folks reading, because it's a non-obvious behavior, especially since to has never worked that way before, whereas a separate function makes it clear.

Without adding a function in addition to opCast that tryTo would then look for on a user-defined type to attempt a conversion (one which returns Nullable!T like tryTo), we probably will have to wrap a call to opCast and catch ConvException if opCast is how the conversion is done so that we can avoid having tryTo throw a ConvException due to opCast using std.conv.to internally, but either way, I think that assuming that the fact that opCast returns a Nullable!T means that it's attempting to convert to T and failing like tryTo would is a bad plan, regardless of how much sense it does or doesn't make to special case to!(Nullable!T) otherwise. It's assuming semantics that may not exist and certainly have not officially existed previously.

--