Type Inference for Struct/Enum Literals

IchorDev zxinsworld at gmail.com
Fri Jul 5 13:19:44 UTC 2024


With language editions on the horizon, I thought it might be 
worth taking another look at this idea, perhaps with a slightly 
broadened scope. Let me know what you think.

### What & why?
For those not familiar, let's say you have a situation like this:
```d
enum Font{ normal, heading, caption }
struct Colour{ float r,g,b,a=1f; }
class TextStyle{
	private Font font_;
	private Colour fg, bg;
	TextStyle font(Font val){ font_ = val; return this; }
	TextStyle foregroundColour(Colour val){ fg = val; return this; }
	TextStyle backgroundColour(Colour val){ bg = val; return this; }
}

void main(){
	auto textStyle = (new TextStyle)
		.font(Font.heading)
		.foregroundColour(Colour(1f, 0.5f, 0.5f))
		.backgroundColour(Colour(0f, 0f, 1f));
}
```
If you look at `main`, you'll see that we're repeating ourselves 
a lot to call those member functions. What if we weren't forced 
to write the types each time? Then it could be more like this:
```d
void main(){
	auto textStyle = (new TextStyle)
		.font(.heading)
		.foregroundColour(.(1f, 0.5f, 0.5f))
		.backgroundColour(.(0f, 0f, 1f));
}
```
Boom, that's it. For any context where an enum literal or struct 
literal is being assigned/passed/etc. to something with a known 
type, allow us to omit the type from the literal. I'm not really 
set on any particular syntax, but here are some thoughts:

- The `.` syntax feels natural for enum literals, but not for 
struct literals. This would have to mean changing the module 
scope operator (perhaps to `./`? (Like to how `./` represents 
'here' in a terminal) because otherwise module-level symbols 
would interfere.
- My original proposal used `$`, which I still like for struct 
literals, but it's a bit random.
- I don't like the C initialiser syntax (`{}`) but it could be 
re-purposed for type-inferred struct literals, which would mean 
we can basically merge the two.

### Implementation
The original enum literal inference DIP was [basically fully 
implemented](https://github.com/dlang/dmd/pull/14650) in the end 
and could easily be resurrected.

Essentially, you should be able to use inference in any situation 
where the compiler can trivially infer the type from the context 
of the literal. Here's a few examples with enums:
```d
enum Enum { foo,bar }
enum Mune { foo,far }

auto x = .bar; //ERR: can't use type inference when there is no 
type specified anywhere!
auto x = Enum.foo;
x = .bar; //OK: `typeof(x)` is an enum with member `bar`
x = .car; //ERR: no member `bar` in enum type `Enum`!

void myFn(Enum x){}

myFn(.bar); //OK: the type of the parameter is an enum with 
member `bar`
myFn(.car); //ERR: no member `bar` in enum type `Enum`!

void myOverload(Enum x){}
void myOverload(Mune x){}

myOverload(.foo); //ERR: `foo` is a member of both `Enum` and 
`Mune`!
myOverload(.bar); //OK: first overload selected based on `Enum` 
having member `bar`
```
The specifics of how this interacts with existing features (e.g. 
array literals) very much depends on how *those* features infer 
type. Here's a good example:
```d
Enum getEnum() => .bar; //OK: return type is an enum with member 
`bar`
Enum x = (() => .bar)(); //it's probably not worth trying to make 
this work
auto y = (int num){
	switch(num){
		case 0: return Enum.foo;
		case 1: return .bar; //this will only work if D's compiler 
frontend knows the return type of this lambda based on the first 
return statement.
		default: assert(0);
	}
}(1);
```

### Other languages
Enum literals having their type contextually inferred is a 
feature in many modern programming languages like 
[Zig](https://ziglang.org/documentation/master/#Enum-Literals), 
[V](https://github.com/vlang/v/blob/master/doc/docs.md#enums), 
[Swift](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/enumerations/), and [Odin](https://odin-lang.org/docs/overview/#implicit-selector-expression). Java has something similar, but it's useless because it only applies to `switch` statements.
For struct literals however, the closest thing I can think of is 
C's struct initialisers.



More information about the dip.ideas mailing list