www.digitalmars.com         C & C++   DMDScript  

digitalmars.dip.ideas - Explicit implicit conversions

reply Quirin Schroll <qs.il.paperinik gmail.com> writes:
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
next sibling parent reply Paul Backus <snarwin gmail.com> writes:
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
parent reply Quirin Schroll <qs.il.paperinik gmail.com> writes:
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 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?
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); ```
Feb 06
parent reply Paul Backus <snarwin gmail.com> writes:
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
parent Walter Bright <newshound2 digitalmars.com> writes:
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
prev sibling parent Walter Bright <newshound2 digitalmars.com> writes:
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