Jump to page: 1 28  
Page
Thread overview
July 20

Hello

I all the time wondered why we always have to be so much verbose with enum, when it's not casting to primitives, it is about repeating their long type name, constantly, all the time

After trying some other languages over the past few years, i discovered in zig you can just ommit the enum type name and just use enums this way: .MY_VALUE

I don't know if that's something that could be supported with D, i am not a compiler dude, so i don't have the answer

Adam on discord mentioned using with(ENUM_TYPE) or just an alias, but i think we go ahead and make it simple

I had prepared a DIP [1], not ready at all, but i wanted to initiate some discussion about that feature

So what do you think? yay? nay? why not?

[1] https://github.com/RUSshy/DIPs/blob/patch-2/DIPs/DIP1xxx.md

July 20
On Tue, Jul 20, 2021 at 03:50:49PM +0000, russhy via Digitalmars-d wrote:
> Hello
> 
> I all the time wondered why we always have to be so much verbose with enum, when it's not casting to primitives, it is about repeating their long type name, constantly, all the time
[...]

OT1H, having to qualify enums by their full name is good, it helps to avoid the mess in C where libraries sometimes define conflicting values under the same name, e.g.,

	// somelib.h
	#define ERROR 0
	#define OK 1

	// someotherlib.h
	#define ERROR -1
	#define OK 0

In D, if you used an enum, you'd have to quality which ERROR or OK you're referring to, which avoids conflicts and also avoids unexpected symbol hijacking. E.g., somelib.h used to define MY_OK = 1, but after upgrading they renamed MY_OK to OK, now your code that referred to OK in someotherlib.h may accidentally get the wrong value.

OTOH, I agree that sometimes D enums become rather verbose. Especially in switch statements where you have to repeat their name for every case. Fortunately, this is where D's `with` statement comes in helpful:

	enum MyEnum { blah, bleh, bluh }
	MyEnum val = ...;
	final switch (val) {
		case MyEnum.blah: ...
		case MyEnum.bleh: ...
		case MyEnum.bluh: ...
	}

can be replaced with:

	enum MyEnum { blah, bleh, bluh }
	MyEnum val = ...;
	final switch (val) with (MyEnum) {
		case blah: ...	// look ma! less repetition!
		case bleh: ...
		case bluh: ...
	}


T

-- 
Questions are the beginning of intelligence, but the fear of God is the beginning of wisdom.
July 20

On Tuesday, 20 July 2021 at 15:50:49 UTC, russhy wrote:

>

So what do you think? yay? nay? why not?

Personally, I have no issue with the current setup. I like knowing exactly where the enum values are coming from, and the existing tools to cut down on the typing (like with) are sufficient.

But if you do want to propose a solution, I don't think .foo is the way to go. It looks too much like the module scope operator:

https://dlang.org/spec/module.html#module_scope_operators

I have no idea what that would mean for the implementation (and you'd need to account for that in the DIP), but visually it would be an unnecessary speed bump when mentally parsing the code.

July 20

On Tuesday, 20 July 2021 at 15:50:49 UTC, russhy wrote:

>

So what do you think? yay? nay? why not?

[1] https://github.com/RUSshy/DIPs/blob/patch-2/DIPs/DIP1xxx.md

Currently, name lookup in D is done independently from type-checking. This proposal would require name lookup to depend on type checking in order to compile code like the following example:

enum A { x }
enum B { x }

A var = .x;

I don't think adding this kind of complexity to the language semantics is worth it just to save a few characters.

July 20
On Tuesday, 20 July 2021 at 16:12:05 UTC, H. S. Teoh wrote:
> On Tue, Jul 20, 2021 at 03:50:49PM +0000, russhy via Digitalmars-d wrote:
>> Hello
>> 
>> I all the time wondered why we always have to be so much verbose with enum, when it's not casting to primitives, it is about repeating their long type name, constantly, all the time
> [...]
>
> OT1H, having to qualify enums by their full name is good, it helps to avoid the mess in C where libraries sometimes define conflicting values under the same name, e.g.,
>
> 	// somelib.h
> 	#define ERROR 0
> 	#define OK 1
>
> 	// someotherlib.h
> 	#define ERROR -1
> 	#define OK 0
>
> In D, if you used an enum, you'd have to quality which ERROR or OK you're referring to, which avoids conflicts and also avoids unexpected symbol hijacking. E.g., somelib.h used to define MY_OK = 1, but after upgrading they renamed MY_OK to OK, now your code that referred to OK in someotherlib.h may accidentally get the wrong value.
>
> OTOH, I agree that sometimes D enums become rather verbose. Especially in switch statements where you have to repeat their name for every case. Fortunately, this is where D's `with` statement comes in helpful:
>
> 	enum MyEnum { blah, bleh, bluh }
> 	MyEnum val = ...;
> 	final switch (val) {
> 		case MyEnum.blah: ...
> 		case MyEnum.bleh: ...
> 		case MyEnum.bluh: ...
> 	}
>
> can be replaced with:
>
> 	enum MyEnum { blah, bleh, bluh }
> 	MyEnum val = ...;
> 	final switch (val) with (MyEnum) {
> 		case blah: ...	// look ma! less repetition!
> 		case bleh: ...
> 		case bluh: ...
> 	}
>
>
> T

That's a good point, and that ``switch with`` is very nice and solve the issue

I didn't know about  ``with`` until adam mentioned it, could the rule in ``switch`` be relaxed and implement something akin to ``with`` ?
July 20
On Tuesday, 20 July 2021 at 16:12:05 UTC, H. S. Teoh wrote:

>
> 	enum MyEnum { blah, bleh, bluh }
> 	MyEnum val = ...;
> 	final switch (val) with (MyEnum) {
> 		case blah: ...	// look ma! less repetition!
> 		case bleh: ...
> 		case bluh: ...
> 	}
>

Cool hack!

July 20
On Tuesday, 20 July 2021 at 16:12:05 UTC, H. S. Teoh wrote:
> On Tue, Jul 20, 2021 at 03:50:49PM +0000, russhy via Digitalmars-d wrote:
>> Hello
>> 
>> I all the time wondered why we always have to be so much verbose with enum, when it's not casting to primitives, it is about repeating their long type name, constantly, all the time
> [...]
>
> OT1H, having to qualify enums by their full name is good, it helps to avoid the mess in C where libraries sometimes define conflicting values under the same name, e.g.,
>
> 	// somelib.h
> 	#define ERROR 0
> 	#define OK 1
>
> 	// someotherlib.h
> 	#define ERROR -1
> 	#define OK 0
>
> In D, if you used an enum, you'd have to quality which ERROR or OK you're referring to, which avoids conflicts and also avoids

That's irrelevant i think, since #defines are like anonymous enums, and you dont need to save typing on those. With named enums in D i think you'd know (most or all? of the time) which one it is by the context... Eg..

enum SomeResult { OK, Error }
enum OtherResult { Error, OK }

You cant assign or pass SomeResult.OK to something that is expecting an OtherResult can you?

So if you have a function...

void handleError(OtherResult r);

and call it thus...

handleError(OK);

You know it's OtherResult.OK

Isnt that the point of strong typing?
July 20
On 7/20/21 1:43 PM, claptrap wrote:

> So if you have a function...
>
> void handleError(OtherResult r);
>
> and call it thus...
>
> handleError(OK);
>
> You know it's OtherResult.OK
>
> Isnt that the point of strong typing?

Most of the time yes, but our enums are not that strong. :) The following compiles and runs with both types.

enum Animal { Jaguar = 100 }
enum Car    { Jaguar = 42 }

void foo(int i) {
}

void main() {
  foo(Animal.Jaguar);
  foo(Car.Jaguar);
}

But the your argument will probably be

  foo(Jaguar);

should work as long as there is no conflict.

But then there is name hijacking. What if that last expression worked and let's say only Car.Jaguar was in scope. What if I imported an unrelated module that defined this:

int Jaguar = 0;

I would expect foo(Jaguar) to still work with that int without complaints because I did not specify the enum version. But then my program works differently. (In other words, I would not expect the compiler to complain about a name conflict between an module-level name and an enum-protected name; that's the whole point anyway.)

In any case, I don't have any problem with extra typing and happy with D's safer enums. I experimented with the with(Enum) helper but don't use it anymore. Not a big deal. :/

Ali

July 20
On Tuesday, 20 July 2021 at 21:15:19 UTC, Ali Çehreli wrote:
> On 7/20/21 1:43 PM, claptrap wrote:
>
> > So if you have a function...
> >
> > void handleError(OtherResult r);
> >
> > and call it thus...
> >
> > handleError(OK);
> >
> > You know it's OtherResult.OK
> >
> > Isnt that the point of strong typing?
>
> Most of the time yes, but our enums are not that strong. :) The following compiles and runs with both types.
>
> enum Animal { Jaguar = 100 }
> enum Car    { Jaguar = 42 }
>
> void foo(int i) {
> }
>
> void main() {
>   foo(Animal.Jaguar);
>   foo(Car.Jaguar);
> }
>
> But the your argument will probably be
>
>   foo(Jaguar);

That that would be an error since the parameter is an 'int' so you dont have any context to decide what enum it should refer to. IE..

void foo(int i);
void bar(Animal a);

foo(Animal.Jaguar); // OK
foo(Jaguar); // Error
bar(Jaguar); // OK

likewise

auto a = Jaguar; // Error
Car c = Jaguar; // OK

The point is you should only be able to drop the enum name if the parameter or the variable you're assigning to are ***named enums***. I suppose for switch you could do the same, if the variable being switched on is a named enum, then the cases can drop the name.


July 20
On 7/20/21 2:52 PM, claptrap wrote:

> The point is you should only be able to drop the enum name if the
> parameter or the variable you're assigning to are ***named enums***.

Agreed. There wouldn't be name hijacking in that case. Either works helpfully or errors with a conflict.

> I
> suppose for switch you could do the same, if the variable being switched
> on is a named enum, then the cases can drop the name.

Agreed with that as well. The type of the switch variable could hint the compiler.

Ali

« First   ‹ Prev
1 2 3 4 5 6 7 8