digitalmars.dip.ideas - Explicit implicit conversions
- Quirin Schroll (58/58) Feb 05 Inspired by [this
- Paul Backus (6/13) Feb 05 Unfortunately this syntax conflicts with constructors and `static
- Quirin Schroll (10/24) Feb 06 The deal is that something that’s that trivial shouldn’t require
- Paul Backus (11/21) Feb 06 Where is all this whining about imports when it comes to other
- Walter Bright (21/24) Feb 13 Spot on.
- Walter Bright (5/7) Feb 06 D doesn't do that because:
Inspired by [this post](https://forum.dlang.org/post/khqsgbrnorbclxhvtpxy forum.dlang.org). The idea is simple: Extend implicit conversions to all types and values. For built-in types, one can do this: `ushort(myUByte)` That works because a `ubyte` can be converted to `ushort`. In some cases, the static type has to be changed by explicitly requesting a conversion that is intended to be fool-proof. (A `cast` would allow for narrowing or other unsafe conversions.) D has many implicit conversions that are “obviously” correct: - Numeric types when the range of the operand is fully included in the target type, e.g.: - `ubyte` → `ushort` → `uint` → `ulong` - `byte` → `short` → `int` → `long` - `ubyte` → `short` - `ushort` → `int` - `uint` → `long` - `float` → `double` → `real` - Adding `const` - Removing function attributes that make guarantees, e.g. ` safe` or `pure`. - Derived to base class or interface The idea is to allow, e.g. `Type(expression)` evaluate to `expression` with a static type of `Type`. Something like this is needed to aid the compiler in figuring out what you want: ```d interface I { } interface J { } class C : I, J { } class D : I, J { } void main() { I[] xs = [new C, new D]; // Error: cannot implicitly convert expression `[new C, new D]` of type `Object[]` to `I[]` } ``` One solution: Use `cast(I)`, but `cast` is not the right tool: It results in `null` for a failed cast, but we want to express the cast shouldn’t fail, and we want an error should we be mistaken. ```d auto xs = [I(new C), new D]; // proposed: good, typeof(xs) is `I[]` ``` Another examples is when you want to control what type is inferred by IFTI: ```d void f(T)(T x, T y) { pragma(msg, T); } f(new C, new D); // Error: template `f` is not callable using argument types `!()(C, D)` f(I(new C), I(new D)); // proposed: good, prints "I" ``` It could be implemented by lowering to `((Type __result) => __result)(expression)`. Of course, that lowering can be provided by a function template: ```d auto ref R implicitCast(R, T)(auto ref T x) => x; ``` The main issue with that is that it’s much wordier.
Feb 05
On Thursday, 6 February 2025 at 02:32:54 UTC, Quirin Schroll wrote:The idea is to allow, e.g. `Type(expression)` evaluate to `expression` with a static type of `Type`.Unfortunately this syntax conflicts with constructors and `static opCall`, so it is probably a non-starter.Of course, that lowering can be provided by a function template: ```d auto ref R implicitCast(R, T)(auto ref T x) => x; ``` The main issue with that is that it’s much wordier.Do we expect this to be used often enough that its length will be a big deal?
Feb 05
On Thursday, 6 February 2025 at 03:03:05 UTC, Paul Backus wrote:On Thursday, 6 February 2025 at 02:32:54 UTC, Quirin Schroll wrote:The deal is that something that’s that trivial shouldn’t require a workaround, not even an easy one. It would have to be in object.d to be easily used, otherwise I’d rather two-line it with a local variable than two-line it with an `import` statement. Really, there’s almost no benefit anymore if you have to import: ```d import std.conv : implicitCast; // meh f(x.implicitCast!R); ```The idea is to allow, e.g. `Type(expression)` evaluate to `expression` with a static type of `Type`.Unfortunately this syntax conflicts with constructors and `static opCall`, so it is probably a non-starter.Of course, that lowering can be provided by a function template: ```d auto ref R implicitCast(R, T)(auto ref T x) => x; ``` The main issue with that is that it’s much wordier.Do we expect this to be used often enough that its length will be a big deal?
Feb 06
On Thursday, 6 February 2025 at 14:45:43 UTC, Quirin Schroll wrote:The deal is that something that’s that trivial shouldn’t require a workaround, not even an easy one. It would have to be in object.d to be easily used, otherwise I’d rather two-line it with a local variable than two-line it with an `import` statement. Really, there’s almost no benefit anymore if you have to import: ```d import std.conv : implicitCast; // meh f(x.implicitCast!R); ```Where is all this whining about imports when it comes to other simple library functions, like `max` or `abs`? It seems to me like "having to import" is something that people love to complain about in DIP threads, but have no trouble with when it comes to actually writing code. In any case, the primary goal here (and with the analogous proposal for reinterpreting casts) is to help D programmers write correct, readable, and maintainable code, not to minimize the number of lines they have to type.
Feb 06
On 2/6/2025 6:52 PM, Paul Backus wrote:In any case, the primary goal here (and with the analogous proposal for reinterpreting casts) is to help D programmers write correct, readable, and maintainable code, not to minimize the number of lines they have to type.Spot on. Consider: ``` const foo = import("foo"); ``` vs ``` import foo; ``` and ``` var x: i32 = 1; comptime var y: i32 = 1; ``` vs ``` int x = 1; enum y = 1; ``` https://ziglang.org/documentation/
Feb 13
On 2/5/2025 6:32 PM, Quirin Schroll wrote:The idea is to allow, e.g. `Type(expression)` evaluate to `expression` with a static type of `Type`.D doesn't do that because: 1. the grammar is ambiguous (confused with function calls) 2. it is not greppable 3. an "is" expression can already do that, and a template can wrap it
Feb 06