Comment #0 by bearophile_hugs — 2014-04-19T10:23:08Z
Currently if you want to call a function sin/cos on a Typedef (here used to define a strongly typed angle), you need a cast:
void main() {
import std.typecons: Typedef;
import std.math: sin;
alias Angle = Typedef!double;
Angle x = 0.5;
auto y1 = sin(x); // Error.
auto y2 = sin(cast(double)x); // OK.
}
So to avoid the unsafe cast I suggest to add a getter to Typedef:
auto y3 = sin(x.get);
Comment #1 by andrej.mitrovich — 2014-04-19T10:35:32Z
Looks fairly innocent, but what if the original type is a user-defined type that already defines the get() method?
Comment #2 by bearophile_hugs — 2014-04-19T12:21:15Z
(In reply to Andrej Mitrovic from comment #1)
> what if the original type is a user-defined type
> that already defines the get() method?
Then perhaps you need to write this?
auto y3 = sin(x.get.get);
Comment #3 by andrej.mitrovich — 2014-04-19T12:23:32Z
(In reply to bearophile_hugs from comment #2)
> (In reply to Andrej Mitrovic from comment #1)
>
> > what if the original type is a user-defined type
> > that already defines the get() method?
>
> Then perhaps you need to write this?
>
> auto y3 = sin(x.get.get);
That would cause existing and generic code to break. For example:
-----
module test;
import std.typecons;
struct S
{
int get() { return 0; }
}
alias S2 = Typedef!S;
void main()
{
S2 s2;
auto x = s2.get(); // works now
}
-----
If we want a solid Typedef in D2 then we have to take this into account as well.
Comment #4 by bearophile_hugs — 2014-04-19T12:47:30Z
(In reply to Andrej Mitrovic from comment #3)
> That would cause existing and generic code to break.
I see. On the other hand currently Typedef has several problems, and all or almost all bug reports/ERs on it are mine, so I doubt there is a lot of existing code that uses it :-)
Comment #5 by andrej.mitrovich — 2014-04-19T12:50:49Z
(In reply to bearophile_hugs from comment #4)
> (In reply to Andrej Mitrovic from comment #3)
>
> > That would cause existing and generic code to break.
>
> I see. On the other hand currently Typedef has several problems, and all or
> almost all bug reports/ERs on it are mine, so I doubt there is a lot of
> existing code that uses it :-)
Yeah I know. But there are plenty of people on IRC that keep asking about it though, I typically don't recommend them to use Typedef since it's full of bugs.
Comment #6 by andrej.mitrovich — 2014-04-22T21:21:52Z
Comment #7 by bearophile_hugs — 2014-04-22T21:38:43Z
(In reply to Andrej Mitrovic from comment #5)
> there are plenty of people on IRC that keep asking about it
> though, I typically don't recommend them to use Typedef since it's full of
> bugs.
While not strictly necessary, something like typedef/Typedef is sometimes useful. In Haskell there's a built-in way to do it, using "newtype", and the GHC compiler has one or more non-standard extensions (GeneralizedNewtypeDeriving) to improve the use of newtype.
(In reply to Andrej Mitrovic from comment #6)
> You seem to have filed https://issues.dlang.org/show_bug.cgi?id=11706
> before, maybe we should just implement that instead? Then the cast would be
> safe.
The need to find the underlying type and the underlying value are both present. So I think we need both enhancements. (I have also just added a note to Issue 11706 ). If you want to implement only one of the two, then I suggest to implement a way to get the underlying value, because then getting the type is one typedef away.
Comment #8 by bearophile_hugs — 2014-04-22T21:40:07Z
(In reply to bearophile_hugs from comment #7)
>because then getting the type is one typedef away.
Sorry, I meant to write one typeof away.
Comment #9 by andrej.mitrovich — 2014-04-24T10:58:47Z
Would this getter give ref access? If so, we might as well make the `Typedef_payload` field public. Otherwise we could make a UFCS function that would avoid symbol conflicts:
-----
V getValue(T)(T t)
/* if (isTypedef!T) */
{
return t.Typedef_payload;
}
-----
This would be usable via:
Typedef!Type x;
x.getValue(); // assuming 'Type' doesn't define getValue
getValue(x); // workaround if 'Type' *does* define getValue
Let me know what you think.
Comment #10 by bearophile_hugs — 2014-04-24T11:21:07Z
(In reply to Andrej Mitrovic from comment #9)
> we might as well make the `Typedef_payload` field public.
Yes, making that field public could be the simplest solution. But the field needs to be renamed to a nicer/shorter name, perhaps like "_data".
> Otherwise we could make a UFCS function that
> would avoid symbol conflicts:
>
> -----
> V getValue(T)(T t)
> /* if (isTypedef!T) */
> {
> return t.Typedef_payload;
> }
> -----
>
> This would be usable via:
>
> Typedef!Type x;
> x.getValue(); // assuming 'Type' doesn't define getValue
> getValue(x); // workaround if 'Type' *does* define getValue
>
> Let me know what you think.
Seems good, but to reduce the frequency of name clashes I'd like a less generic/common name, like "typedefVal". And if possible it should return by reference:
Typedef!Int x;
x.typedefVal++;
Comment #11 by monkeyworks12 — 2014-09-24T21:26:33Z
I think this enhancement request should be closed now that https://github.com/D-Programming-Language/phobos/pull/2116 has been merged. With that PR, your first example now becomes:
void main() {
import std.typecons: Typedef;
import std.math: sin;
alias Angle = Typedef!double;
Angle x = 0.5;
auto y1 = sin(x); // Error.
auto y2 = sin(cast(TypedefType!Angle)x); // OK.
}
A bit verbose, but it accomplishes what you want and is more DRY and safer than cast(double)x.
Comment #12 by bearophile_hugs — 2014-09-25T21:09:19Z
(In reply to monkeyworks12 from comment #11)
> I think this enhancement request should be closed now that
> https://github.com/D-Programming-Language/phobos/pull/2116 has been merged.
> With that PR, your first example now becomes:
>
> void main() {
> import std.typecons: Typedef;
> import std.math: sin;
> alias Angle = Typedef!double;
> Angle x = 0.5;
> auto y1 = sin(x); // Error.
> auto y2 = sin(cast(TypedefType!Angle)x); // OK.
> }
>
> A bit verbose, but it accomplishes what you want and is more DRY and safer
> than cast(double)x.
This ER asks for a function like "typedefVal" that's usable like:
auto y2 = x.typedefVal.sin; // OK.
Casts are unsafe, their usage should be minimized in D code. So this ER is still valid.
Comment #13 by monkeyworks12 — 2014-09-26T04:24:56Z
(In reply to bearophile_hugs from comment #12)
> (In reply to monkeyworks12 from comment #11)
> > I think this enhancement request should be closed now that
> > https://github.com/D-Programming-Language/phobos/pull/2116 has been merged.
> > With that PR, your first example now becomes:
> >
> > void main() {
> > import std.typecons: Typedef;
> > import std.math: sin;
> > alias Angle = Typedef!double;
> > Angle x = 0.5;
> > auto y1 = sin(x); // Error.
> > auto y2 = sin(cast(TypedefType!Angle)x); // OK.
> > }
> >
> > A bit verbose, but it accomplishes what you want and is more DRY and safer
> > than cast(double)x.
>
> This ER asks for a function like "typedefVal" that's usable like:
>
> auto y2 = x.typedefVal.sin; // OK.
>
> Casts are unsafe, their usage should be minimized in D code. So this ER is
> still valid.
But casting to TypedefType!(typeof(x)) is always safe, so if you want such a function, it's trivial to add one yourself. The main problem (getting the underlying type of a Typedef) is solved.
auto typedefVal(T)(T val)
{
return cast(TypedefType!T)val;
}
Comment #14 by bearophile_hugs — 2014-09-26T08:50:22Z
(In reply to monkeyworks12 from comment #13)
> But casting to TypedefType!(typeof(x)) is always safe,
If it's always safe it shouldn't look dangerous, so it it shouldn't look like "cast(" in a textual search.
> so if you want such a function, it's trivial to add one yourself.
> The main problem (getting the underlying type of a Typedef) is solved.
>
> auto typedefVal(T)(T val)
> {
> return cast(TypedefType!T)val;
> }
Yes, this ER asks for such safe function in Phobos.
Comment #15 by bearophile_hugs — 2014-09-26T10:07:25Z
(In reply to bearophile_hugs from comment #14)
> > auto typedefVal(T)(T val)
> > {
> > return cast(TypedefType!T)val;
> > }
>
> Yes, this ER asks for such safe function in Phobos.
But it's better to add a template constraint to that T to be sure it's a Typedef.
Comment #16 by Bastiaan — 2018-03-13T12:40:33Z
(In reply to bearophile_hugs from comment #15)
> (In reply to bearophile_hugs from comment #14)
>
> > > auto typedefVal(T)(T val)
> > > {
> > > return cast(TypedefType!T)val;
> > > }
> >
> > Yes, this ER asks for such safe function in Phobos.
>
> But it's better to add a template constraint to that T to be sure it's a
> Typedef.
No need:
/// Instantiating with a non-Typedef will return that type
static assert(is(TypedefType!int == int));
Comment #17 by josipp — 2022-04-03T19:13:39Z
(In reply to bearophile_hugs from comment #14)
> (In reply to monkeyworks12 from comment #13)
>
> > But casting to TypedefType!(typeof(x)) is always safe,
>
> If it's always safe it shouldn't look dangerous, so it it shouldn't look
> like "cast(" in a textual search.
>
>
> > so if you want such a function, it's trivial to add one yourself.
> > The main problem (getting the underlying type of a Typedef) is solved.
> >
> > auto typedefVal(T)(T val)
> > {
> > return cast(TypedefType!T)val;
> > }
>
> Yes, this ER asks for such safe function in Phobos.
Could this be integrated into the definition of `std.conv.to(T)`?
The module `std.conv` is described as a one-stop-shop for converting
different types into each other, after all.
I see a very clear objective here to safely and explicitly cast the underlying
value to its corresponding type, and the `to` template is very well-suited
for that kind of transformation.
Comment #18 by robert.schadek — 2024-12-01T16:20:50Z