www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Methods in enums

reply WebFreak001 <d.forum webfreak.org> writes:
We have UFCS in D, which allows us to define global methods that 
just take for example an enum as first argument, and then call 
that global method using syntax `enumArg.globalMethod(rest)` 
instead of `globalMethod(enumArg, rest)`. I think this is very 
useful for lots of scenarios.

However enums themselves can't contain any methods. I think for 
consistency with the other types, such as union, struct, 
interface, class, etc. it would be nice if enums could just 
contain member methods, similarly to how structs and unions work. 
You can't put any methods inside C structs, but D allows it, so 
why not also for enums?

This would allow selective imports for enums, e.g. `import 
somemod : MyEnum;` to also pull in all the global UFCS functions. 
Nowadays IDEs partially make this obsolete, especially if you 
already know the names of the "member methods" you can call on 
the enum, since they would auto-import this for you. But I think 
D's design allowing a lot of code writing without IDE would fit 
this feature very well, while also benefiting IDE users for 
automatically having all built-in custom enum functions available 
to aid discovery, especially of new libraries. Additionally 
static string parsing methods would naturally fit in there and 
also allow defining special methods such as toString or other 
special things that code such as std.conv uses.

Is there possibly a major reason why enums couldn't contain 
methods? What are your thoughts?

They would of course need a bit of new syntax, like possibly 
separating the enum values and methods with `;`, like what Java 
does, or having a more D-like syntax that just allows methods 
anywhere in the enum. I think the concept itself would fit into 
the language design very nicely.
Aug 14 2023
next sibling parent reply Alexandru Ermicioi <alexandru.ermicioi gmail.com> writes:
On Monday, 14 August 2023 at 23:40:33 UTC, WebFreak001 wrote:
 ...
 They would of course need a bit of new syntax, like possibly 
 separating the enum values and methods with `;`, like what Java 
 does, or having a more D-like syntax that just allows methods 
 anywhere in the enum. I think the concept itself would fit into 
 the language design very nicely.
You could use plain private struct as an enum type, then you'd have methods on enum members. Best regards, Alexandru.
Aug 14 2023
parent "H. S. Teoh" <hsteoh qfbox.info> writes:
On Tue, Aug 15, 2023 at 12:38:38AM +0000, Alexandru Ermicioi via Digitalmars-d
wrote:
 On Monday, 14 August 2023 at 23:40:33 UTC, WebFreak001 wrote:
 ...
 They would of course need a bit of new syntax, like possibly
 separating the enum values and methods with `;`, like what Java
 does, or having a more D-like syntax that just allows methods
 anywhere in the enum. I think the concept itself would fit into the
 language design very nicely.
You could use plain private struct as an enum type, then you'd have methods on enum members.
[...] Proof of concept: struct EnumMember { int value; void myMethod() { import std.stdio; writefln("myMethod: %s", value); } } enum MyEnum : EnumMember { hooray = EnumMember(1), boo = EnumMember(2), haha = EnumMember(3), } void main() { MyEnum e; e.myMethod(); } T -- The diminished 7th chord is the most flexible and fear-instilling chord. Use it often, use it unsparingly, to subdue your listeners into submission!
Aug 14 2023
prev sibling next sibling parent reply sighoya <sighoya gmail.com> writes:
First, thanks Teoh and Alexandru for presenting a possible 
workaround

However, what we also want are methods taking the enum itself as 
argument, which isn't possible with the solutions presented so 
far because of cyclicity.

So +1 from me for a possible DIP to get it done.

On Monday, 14 August 2023 at 23:40:33 UTC, WebFreak001 wrote:
 This would allow selective imports for enums, e.g. `import 
 somemod : MyEnum;` to also pull in all the global UFCS
Can you elaborate more? Why are enum functions ufcs? And is it possible to import ufcs functions for a specific imported symbol? Would interest me.
Aug 15 2023
next sibling parent reply WebFreak001 <d.forum webfreak.org> writes:
On Tuesday, 15 August 2023 at 10:52:00 UTC, sighoya wrote:
 First, thanks Teoh and Alexandru for presenting a possible 
 workaround

 However, what we also want are methods taking the enum itself 
 as argument, which isn't possible with the solutions presented 
 so far because of cyclicity.

 So +1 from me for a possible DIP to get it done.

 On Monday, 14 August 2023 at 23:40:33 UTC, WebFreak001 wrote:
 This would allow selective imports for enums, e.g. `import 
 somemod : MyEnum;` to also pull in all the global UFCS
Can you elaborate more? Why are enum functions ufcs? And is it possible to import ufcs functions for a specific imported symbol? Would interest me.
with that sentence I meant we could put the methods that used to be global functions that you'd usually want to call with UFCS into the actual symbol, so they are callable only as child methods. Then a selective import like `import somemod : MyEnum;` would have all the myEnum.xyz functions already available in code, while currently you would need to add `import somemod : MyEnum, xyz;` where `xyz` is a global method that takes a MyEnum as first argument
Aug 16 2023
parent Alexandru Ermicioi <alexandru.ermicioi gmail.com> writes:
On Wednesday, 16 August 2023 at 12:22:57 UTC, WebFreak001 wrote:
 ...
 with that sentence I meant we could put the methods that used 
 to be global functions that you'd usually want to call with 
 UFCS into the actual symbol, so they are callable only as child 
 methods. Then a selective import like `import somemod : 
 MyEnum;` would have all the myEnum.xyz functions already 
 available in code, while currently you would need to add 
 `import somemod : MyEnum, xyz;` where `xyz` is a global method 
 that takes a MyEnum as first argument
Then just put those methods inside struct used in enum, and there'll be automatic import of those methods. Btw this is also what java enum members are, i.e. plain objects of specific type guaranteed to be only n instances during a jvm lifetime. Best regards, Alexandru.
Aug 17 2023
prev sibling parent reply Alexandru Ermicioi <alexandru.ermicioi gmail.com> writes:
On Tuesday, 15 August 2023 at 10:52:00 UTC, sighoya wrote:
 First, thanks Teoh and Alexandru for presenting a possible 
 workaround

 However, what we also want are methods taking the enum itself 
 as argument, which isn't possible with the solutions presented 
 so far because of cyclicity.
If you need to know which member of enum that particular instance of a struct is, that has it's method invoked, then you could check for this inside function body of said method, by looking to which enum member this instance is identical to by either memory address, or equality. Btw there should not be any problems with self reference in case if you have enum type as an argument to a method inside struct imho. Best regards, Alexandru.
Aug 17 2023
parent reply sighoya <sighoya gmail.com> writes:
On Friday, 18 August 2023 at 00:25:13 UTC, Alexandru Ermicioi 
wrote:
 Btw there should not be any problems with self reference in 
 case if you have enum type as an argument to a method inside 
 struct imho.

 Best regards,
 Alexandru.
How can I call a method like myEnumInstance.method(arg) when using structs in enums?
Aug 18 2023
parent Alexandru Ermicioi <alexandru.ermicioi gmail.com> writes:
On Friday, 18 August 2023 at 10:48:45 UTC, sighoya wrote:
 How can I call a method like myEnumInstance.method(arg) when 
 using structs in enums?
Easy-peasy: ```d import std; /** * Note this type and enum ideally should be in separate module, to properly hide VoldemortType from rest of the code. **/ private struct VoldemortType { private string message; public void say(string post) const { writeln(message, " ", post); } public bool opEquals(Message other) const { return other.message == this.message; } public void sayAlso(Message other, string forSubject) const { this.say(forSubject); other.say(forSubject); } } public enum Message : VoldemortType { hello = VoldemortType("hello"), bye = VoldemortType("bye") } void decorate(Message message, string decoration, string subject) { std.stdio.write(decoration, " "); message.say(subject ~ " " ~ decoration); } void main() { Message mess = Message.hello; mess.say("entity"); mess.sayAlso(Message.bye, "subject"); mess.decorate("**", "third-party"); writeln("Is hello message same as bye?: ", Message.hello == Message.bye); writeln("Is mess variable a hello message?: ", mess == Message.hello); } ``` As you can see, there is no such cyclicity you've mentioned, and using `Message` as parameter to a method or free function (for ufcs), doesn't cause any issues. Still for free functions, you'd have to import them separately from enum, which imho is ok, since you can know that it is an extension, and not a function part of enum behavior. Best regards, Alexandru.
Aug 18 2023
prev sibling parent reply user1234 <user1234 12.de> writes:
On Monday, 14 August 2023 at 23:40:33 UTC, WebFreak001 wrote:
 We have UFCS in D, which allows us to define global methods 
 that just take for example an enum as first argument, and then 
 call that global method using syntax 
 `enumArg.globalMethod(rest)` instead of `globalMethod(enumArg, 
 rest)`. I think this is very useful for lots of scenarios.

 However enums themselves can't contain any methods. I think for 
 consistency with the other types, such as union, struct, 
 interface, class, etc. it would be nice if enums could just 
 contain member methods, similarly to how structs and unions 
 work. You can't put any methods inside C structs, but D allows 
 it, so why not also for enums?

 This would allow selective imports for enums, e.g. `import 
 somemod : MyEnum;` to also pull in all the global UFCS 
 functions. Nowadays IDEs partially make this obsolete, 
 especially if you already know the names of the "member 
 methods" you can call on the enum, since they would auto-import 
 this for you. But I think D's design allowing a lot of code 
 writing without IDE would fit this feature very well, while 
 also benefiting IDE users for automatically having all built-in 
 custom enum functions available to aid discovery, especially of 
 new libraries. Additionally static string parsing methods would 
 naturally fit in there and also allow defining special methods 
 such as toString or other special things that code such as 
 std.conv uses.

 Is there possibly a major reason why enums couldn't contain 
 methods? What are your thoughts?

 They would of course need a bit of new syntax, like possibly 
 separating the enum values and methods with `;`, like what Java 
 does, or having a more D-like syntax that just allows methods 
 anywhere in the enum. I think the concept itself would fit into 
 the language design very nicely.
you can do that with a mixin declaration + heavy introspection code, something like ``` mixin(generateSelectiveImportForTypeAndUFCSCapableFunctionOf!("someModule", "someType")()) ``` However one thing I have observed, in the past, is that even if you make the metaprog-equivalent code of a compiler-grade feature, people wont use it. So at some point the question is more: should this be builtin (e.g as a __traits) or be in std.typecons/std.traits ? Big question.
Aug 18 2023
parent reply sighoya <sighoya gmail.com> writes:
On Friday, 18 August 2023 at 11:21:57 UTC, user1234 wrote:

 ```
 mixin(generateSelectiveImportForTypeAndUFCSCapableFunctionOf!("someModule",
"someType")())
 ```

 However one thing I have observed, in the past, is that even if 
 you make the metaprog-equivalent code of a compiler-grade 
 feature, people wont use it. So at some point the question is 
 more: should this be builtin (e.g as a __traits) or be in 
 std.typecons/std.traits ?

 Big question.
Yes, but then you have again the problem of importing methods separately to types.
Aug 18 2023
parent reply user1234 <user1234 12.de> writes:
On Friday, 18 August 2023 at 12:11:04 UTC, sighoya wrote:
 On Friday, 18 August 2023 at 11:21:57 UTC, user1234 wrote:

 ```
 mixin(generateSelectiveImportForTypeAndUFCSCapableFunctionOf!("someModule",
"someType")())
 ```

 However one thing I have observed, in the past, is that even 
 if you make the metaprog-equivalent code of a compiler-grade 
 feature, people wont use it. So at some point the question is 
 more: should this be builtin (e.g as a __traits) or be in 
 std.typecons/std.traits ?

 Big question.
Yes, but then you have again the problem of importing methods separately to types.
Yes. The metaprog in the mixin hides the import, just like what will do a compiler. "I can find this, but is this result correct given the scope ?".
Aug 18 2023
parent sighoya <sighoya gmail.com> writes:
On Friday, 18 August 2023 at 16:30:00 UTC, user1234 wrote:
 Yes. The metaprog in the mixin hides the import, just like what 
 will do a compiler.
 "I can find this, but is this result correct given the scope ?".
Good idea. There are some points which suck however: - additional code generation time - enum variable declarations need separate mixin call to include ufcs as well. I think maybe a more general case allowing importing a type with ufcs methods as well would be a better solution. However, for methods associated to the enum type instead of the enum value it would be better to allow them to be included in the enum akin how we do it with structs.
Aug 18 2023