Jump to page: 1 2
Thread overview
Subtyping of an enum
Apr 15
Alex
Apr 15
Alex
Apr 15
Alex
Apr 15
XavierAP
Apr 15
XavierAP
Apr 15
XavierAP
Apr 15
XavierAP
Apr 15
diniz
Apr 15
Alex
Apr 15
Alex
Apr 15
Alex
April 15
Hello! I am currently trying to add a custom `toString` method to an enum so that:
1. Enum members would still have numeric values and can be easily compared (things like `enum a { foo = "FOO", bar = "BAR”}` won't do, I want `a.foo < a.bar)`)
2. More custom methods can be implemented in the future

Obvious solution is to wrap an enum in a structure and utilize 'alias this' for subtyping like this:
```
struct Enum {
  private {
    enum internal {
      foo,
      bar
    }
    internal m_enum;
  }
  this(internal i) { m_enum = i; }
  alias m_enum this;
  string toString() {
    // custom implementation of toString
  }
}
```

This seems to work just fine for assigning and comparisons but passing Enum as a function argument does not work:
```
void fun(Enum e) {}

fun(Enum.foo);
---
Error: function fun(Enum e) is not callable using argument types (internal)
Cannot pass argument foo of type internal to parameter Enum e.
```

Of course, I could just define a bunch of functions that accept my enum as the first argument and call them using UFCS but it'd require to explicitly specify functions instead of D taking care of that (toString() for structures is called automagically by functions like writeln) and those functions would hang around here and there totally unorganized. I prefer to keep functions inside of structures and classes.

If there are other ways of achieving the same *and* keeping code clean and organized, please share.

Thank you in advance,
Anton.

April 15
On Monday, 15 April 2019 at 08:39:24 UTC, Anton Fediushin wrote:
> Hello! I am currently trying to add a custom `toString` method to an enum so that:
> 1. Enum members would still have numeric values and can be easily compared (things like `enum a { foo = "FOO", bar = "BAR”}` won't do, I want `a.foo < a.bar)`)
> 2. More custom methods can be implemented in the future
>
> Obvious solution is to wrap an enum in a structure and utilize 'alias this' for subtyping like this:
> ```
> struct Enum {
>   private {
>     enum internal {
>       foo,
>       bar
>     }
>     internal m_enum;
>   }
>   this(internal i) { m_enum = i; }
>   alias m_enum this;
>   string toString() {
>     // custom implementation of toString
>   }
> }
> ```
>
> This seems to work just fine for assigning and comparisons but passing Enum as a function argument does not work:
> ```
> void fun(Enum e) {}
>
> fun(Enum.foo);
> ---
> Error: function fun(Enum e) is not callable using argument types (internal)
> Cannot pass argument foo of type internal to parameter Enum e.
> ```
>
> Of course, I could just define a bunch of functions that accept my enum as the first argument and call them using UFCS but it'd require to explicitly specify functions instead of D taking care of that (toString() for structures is called automagically by functions like writeln) and those functions would hang around here and there totally unorganized. I prefer to keep functions inside of structures and classes.

Isn't this a reason for passing your internals encapsulated, like

´´´
Enum EnumInstance = Enum(Enum.internal.foo);
fun(EnumInstance);
´´´

?

>
> If there are other ways of achieving the same *and* keeping code clean and organized, please share.
>
> Thank you in advance,
> Anton.

Otherwise, you could alwas define fun as

´´´
void fun(Enum.internal e) {}
´´´

but I assume, you want to avoid especially this.

In favor of my first proposition, also speaks the fact, that Enum.foo is somewhat awkward w.r.t. your question, as you treat the internal enum as a static member. Was this intended?

April 15
On Monday, 15 April 2019 at 08:39:24 UTC, Anton Fediushin wrote:
>
> Hello! I am currently trying to add a custom `toString` method

Several remarks... First of all, strings can be compared (alphabetically) as well as integers, e.g.
assert("foo" > "bar")
Perhaps not your use case, but worth noting.

You have defined your sub-typing the opposite way that you wanted it to work: every `Enum` is an `internal`, but the other way around an `internal` may not work as an `Enum`. Your `fun` would in principle work if it were defined with an `internal` but passed an `Enum`... Of course you have defined `internal` as nested private so no... But then how did you want anything to work if no one outside Enum knows the super-type?

You obviously need to re-think your problem and your design :)

> Obvious solution is to wrap an enum in a structure and utilize 'alias this' for subtyping like this:

Actually the obvious solution (not sure if it otherwise works for you) would be to take advantage of D's Uniform Function Call Syntax [1] and define toString as a global function that can be called as a method:

enum Fubar { foo, bar }

string toString(Fubar fb)
{
	return "It works.";
}

void main()
{
	import std.stdio;
	writeln(Fubar.foo.toString);
}

_________
[1] https://tour.dlang.org/tour/en/gems/uniform-function-call-syntax-ufcs
April 15
On Monday, 15 April 2019 at 10:00:36 UTC, Alex wrote:
> On Monday, 15 April 2019 at 08:39:24 UTC, Anton Fediushin wrote:
>> [snip]
>
> Otherwise, you could alwas define fun as
>
> ´´´
> void fun(Enum.internal e) {}
> ´´´
>
> but I assume, you want to avoid especially this.
>
> In favor of my first proposition, also speaks the fact, that Enum.foo is somewhat awkward w.r.t. your question, as you treat the internal enum as a static member. Was this intended?

Enum.internal is private to make it inaccessible from any other place. All I want is a way to have an enum that I could extend with my own methods.

Something to make the following code work:
```
Enum a = Enum.foo;
Enum b = Enum.bar;
assert(a == Enum.foo);
assert(a < b);
assert(a.toString == "FOO");
assert(b.toString == "BAR");
writeln(a); // FOO
writeln(b); // BAR
```


April 15
On Monday, 15 April 2019 at 10:06:30 UTC, XavierAP wrote:
> On Monday, 15 April 2019 at 08:39:24 UTC, Anton Fediushin wrote:
>>
>> Hello! I am currently trying to add a custom `toString` method
>
> Several remarks... First of all, strings can be compared (alphabetically) as well as integers, e.g.
> assert("foo" > "bar")
> Perhaps not your use case, but worth noting.

I already know that but defining enum with strings would break my code:
```
assert(Enum.foo < Enum.bar);
```
Would never succeed.

>
> You have defined your sub-typing the opposite way that you wanted it to work: every `Enum` is an `internal`, but the other way around an `internal` may not work as an `Enum`. Your `fun` would in principle work if it were defined with an `internal` but passed an `Enum`... Of course you have defined `internal` as nested private so no... But then how did you want anything to work if no one outside Enum knows the super-type?

Isn't this how subtyping works for integers and other types? For example, you have subtyped an integer and added some new methods to it?

>
> You obviously need to re-think your problem and your design :)

The problem here is that I want to keep methods that are related to an enum inside of this enum for purely aesthetic and organizational purposes.

>
>> Obvious solution is to wrap an enum in a structure and utilize 'alias this' for subtyping like this:
>
> Actually the obvious solution (not sure if it otherwise works for you) would be to take advantage of D's Uniform Function Call Syntax [1] and define toString as a global function that can be called as a method:
>
> enum Fubar { foo, bar }
>
> string toString(Fubar fb)
> {
> 	return "It works.";
> }
>
> void main()
> {
> 	import std.stdio;
> 	writeln(Fubar.foo.toString);
> }
>
> _________
> [1] https://tour.dlang.org/tour/en/gems/uniform-function-call-syntax-ufcs

This is what I am doing now, I was just curious if there was a better solution. These global functions pollute global namespace. I know that I could put them into an own module and 'public import' just my enum because these methods would rarely be used by the other components of my application. If I would want to use them though, I'd have to import that ugly module and never forget to call `writeln(a.toString)` instead of `writeln(a)` or else it'll do not what I wanted.

And once more, what I want to achieve is purely about overall code organization. I mean, if structs (data) can have functions (methods) that are performed on them, why an enum (single value) cannot have own methods performed on it?


April 15
On Monday, 15 April 2019 at 10:15:50 UTC, Anton Fediushin wrote:
> On Monday, 15 April 2019 at 10:00:36 UTC, Alex wrote:
>
> Enum.internal is private to make it inaccessible from any other place. All I want is a way to have an enum that I could extend with my own methods.
>
> Something to make the following code work:
> ```
> Enum a = Enum.foo;
> Enum b = Enum.bar;
> assert(a == Enum.foo);
> assert(a < b);
> assert(a.toString == "FOO");
> assert(b.toString == "BAR");
> writeln(a); // FOO
> writeln(b); // BAR
> ```

This would:

´´´

struct Enum {
  private {
    enum internal {
      foo,
      bar
    }
    internal m_enum;
  }
  this(internal i) { m_enum = i; }
  alias m_enum this;
  string toString() {
    if(m_enum == internal.foo)
        return "FOO";
    else
        return "BAR";
  }
}

void fun(Enum e) {}


void main(){
    import std.stdio;
    fun(Enum.init);
    Enum a = Enum.foo;
    Enum b = Enum.bar;
    assert(a == Enum.foo);
    assert(a < b);
    assert(a.toString == "FOO");
    assert(b.toString == "BAR");
    writeln(a); // FOO
    writeln(b); // BAR
}
´´´

Assuming, that automatic generation of "FOO" from foo was not part of your question :-p
April 15
On Monday, 15 April 2019 at 10:34:42 UTC, Anton Fediushin wrote:
> On Monday, 15 April 2019 at 10:06:30 UTC, XavierAP wrote:
>>
>> You have defined your sub-typing the opposite way that you wanted it to work: every `Enum` is an `internal`, but the other way around an `internal` may not work as an `Enum`. Your `fun` would in principle work if it were defined with an `internal` but passed an `Enum`... Of course you have defined `internal` as nested private so no... But then how did you want anything to work if no one outside Enum knows the super-type?
>
> Isn't this how subtyping works for integers and other types? For example, you have subtyped an integer and added some new methods to it?

Yes (leaving aside whether stuff is private or nested) but you are using the types' relationship the other way around. You have:

static assert(is(Enum : internal));

But you are defining and calling fun() as if it were the other way around (internal : Enum)

April 15
On Monday, 15 April 2019 at 10:34:42 UTC, Anton Fediushin wrote:
>
> The problem here is that I want to keep methods that are related to an enum inside of this enum for purely aesthetic and organizational purposes.
> ...
> These global functions pollute global namespace.

If you have defined `x.toString` as a method, UFCS means of course you can also call `toString(x)`... So it's debatable what polluting means.

More generally you insist on modules and namespaces to be different concepts, which they are (pointlessly) for C++, but not for D (purposely).
April 15
On Monday, 15 April 2019 at 12:38:59 UTC, XavierAP wrote:
>
> More generally you insist on modules and namespaces to be different concepts, which they are (pointlessly) for C++, but not for D (purposely).

Here I should say packages instead of modules... but the general argument stays.

Anyway your design is up to you :) but sub-typing is not reflexive, in D or any language.
April 15
On Monday, 15 April 2019 at 10:45:26 UTC, Alex wrote:
> On Monday, 15 April 2019 at 10:15:50 UTC, Anton Fediushin wrote:
>> On Monday, 15 April 2019 at 10:00:36 UTC, Alex wrote:
>> [snip]
>
> This would:
>
> ´´´
>
> struct Enum {
>   private {
>     enum internal {
>       foo,
>       bar
>     }
>     internal m_enum;
>   }
>   this(internal i) { m_enum = i; }
>   alias m_enum this;
>   string toString() {
>     if(m_enum == internal.foo)
>         return "FOO";
>     else
>         return "BAR";
>   }
> }
>
> void fun(Enum e) {}
>
>
> void main(){
>     import std.stdio;
>     fun(Enum.init);
>     Enum a = Enum.foo;
>     Enum b = Enum.bar;
>     assert(a == Enum.foo);
>     assert(a < b);
>     assert(a.toString == "FOO");
>     assert(b.toString == "BAR");
>     writeln(a); // FOO
>     writeln(b); // BAR
> }
> ´´´
>
> Assuming, that automatic generation of "FOO" from foo was not part of your question :-p

This does work unless I want to use it like this:
```
fun(Enum.foo);
---
Error: function fun(Enum e) is not callable using argument types (internal)
cannot pass argument foo of type internal to parameter Enum e
```



« First   ‹ Prev
1 2