Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enum member method/variables #10

Closed
kevinresol opened this issue Oct 27, 2016 · 18 comments
Closed

Enum member method/variables #10

kevinresol opened this issue Oct 27, 2016 · 18 comments

Comments

@kevinresol
Copy link

kevinresol commented Oct 27, 2016

In language like Java you can declare methods/variables for enum right inside the enum itself:

Something like:

public enum Direction {

    NORTH, SOUTH, EAST, WEST;

    private Direction opposite;

    static {
        NORTH.opposite = SOUTH;
        SOUTH.opposite = NORTH;
        EAST.opposite = WEST;
        WEST.opposite = EAST;
    }

    public Direction getOppositeDirection() {
        return opposite;
    }
}

This looks handy but I am not quite sure about the difficulty of implementing it and any negative consequences to the language. So I would like to gain some more knowledge about the topic before writing a proposal

@markknol
Copy link
Member

The parameters of an enum can hold any data, so you need to create variables out of the enum. I think what you are asking can be done, but with a different architecture:

using Test.Directions;

class Test {
    static function main() trace(Directions.EAST.getOpposite()); // West
}

class Directions {
  public static var NORTH:Direction = WithOpposite(South);
  public static var SOUTH:Direction = WithOpposite(North);
  public static var EAST:Direction = WithOpposite(West);
  public static var WEST:Direction = WithOpposite(East);

  public static function getOpposite(d:Direction) {
    return d.getParameters()[0];
  }
}

enum Direction {
   WithOpposite(opposite:Direction);
   North();
   South();
   East();
   West();
}

http://try.haxe.org/#8F223

@kevinresol
Copy link
Author

@markknol Thanks for the example. But I understand that these functionalities can be achieved with static extension (actually abstract will do the job even better).

My point here is that it would be more convenience to code, because in that case people doesn't need to write the using statement or the abstract code.

@back2dos
Copy link
Member

Generally, I think what Kevin is after is to be able to just add map to something like haxe.ds.Option. Static extensions are nice and all, but being able to add some rudimentary functionality to an enum would be nice.

enum Option<T> {

  Some(value:T);
  None;

  public inline function map<R>(f:T->R)
    return switch this {
      case Some(v): Some(f(v));
      case None: Nonel
    }
}

Or for the above example:

enum Direction {

  North;
  South;
  East;
  West;

  public var opposite(get, never):Direction;
  inline function get_opposite() 
    return switch this {
      case North: South;
      case South: North;
      case East: West;
      case West: East;
    }
}

@kevinresol
Copy link
Author

static vars will be nice too

enum Option<T> {
  public static var ZERO:Option<Int> = Some(0);

  Some(value:T);
  None;
}

@back2dos
Copy link
Member

Well, yeah, down the line having all the bells and whistles of abstracts on enums would be good.

One can do abstracts over enums to have almost the same, except that pattern matching becomes a PITA and generally anything that has to do with constructors just fails.

@kevinresol
Copy link
Author

One can do abstracts over enums to have almost the same, except that pattern matching becomes a PITA and generally anything that has to do with constructors just fails.

And I have attempted to do the exact thing many times. 😄

@RealyUniqueName
Copy link
Member

pattern matching becomes a PITA and generally anything that has to do with constructors just fails.

You can add typedef OptionImpl<T> = haxe.ds.Option<T>; to a module where your abstract is defined to fix your examples.

@back2dos
Copy link
Member

pattern matching becomes a PITA and generally anything that has to do with constructors just fails.

You can add typedef OptionImpl<T> = haxe.ds.Option<T>; to a module where your abstract is defined to fix your examples.

You are presuming here that I am importing the whole module that contains the abstract. I could be:

  1. referring to the abstract by fully qualified path rather than import its whole module. Works fine with enums, but not with abstract over enum.
  2. destructuring the return value of a function without having the return type imported. Also works fine with enums, fails with abstract over enum.
  3. calling a function that consumes the abstract, without wanting to import anything. Again, works just fine when the argument type is the enum, fails with abstract over enum.

One solution would be to add a little special treatment for abstracts over enums to make them behave exactly the same (IIRC there's already an open issues for that). It would be good enough for me. Then again:

enum XImpl<T, U, V> { 
  /* constructors */ 
}
abstract X<T, U, V>(XImpl<T, U, V>) from XImpl<T, U, V> to XImpl<T, U, V> {
  /* some candy */
}

vs.

enum X<T, U, V> {
  /* constructors */ 
  /* some candy */
}

I struggle to see the benefit in the verbosity of the former. The the contrary, I would claim it's less clear: practically speaking X is the underlying enum and the noise just obscures that intent. One can still make "opaque" abstracts (i.e. without from / to) over enums if that is so desired. But if one really wants to express the latter, making them jump through hoops doesn't help them nor those reading the code.

@Simn
Copy link
Member

Simn commented Jun 1, 2017

This would indeed be nice, but also require quite a few changes. I'm not sure I'll have the time to look into this...

@kevinresol
Copy link
Author

Do I need to start a Haxe Project for this?

@Simn
Copy link
Member

Simn commented Oct 15, 2017

Don't think so, this is more about actually implementing it.

@gene-pavlovsky
Copy link

This sounds like a very useful feature.

@kevinresol
Copy link
Author

kevinresol commented Sep 5, 2018

This would indeed be nice, but also require quite a few changes. I'm not sure I'll have the time to look into this...

Instead of implementing stuff inside enum{}, would it be easier to make abstract-over-enum behaves the same as normal enum as mentioned by @back2dos? It is mostly about exposing/inferring the enum constructors and don't let them buried by the abstract wrapping.

Proper enum members could be next step.

@Simn
Copy link
Member

Simn commented Sep 21, 2018

We decided that we don't want this particular feature. However, we'll look into supporting some notion of auto-using which can be applied to type and is going to work pretty much the same way.

@Simn Simn closed this as completed Sep 21, 2018
@kevinresol
Copy link
Author

How about the special caring for abstract-over-enum?

@Simn
Copy link
Member

Simn commented Sep 21, 2018

What do you mean exactly?

@kevinresol
Copy link
Author

You are presuming here that I am importing the whole module that contains the abstract. I could be:

  1. referring to the abstract by fully qualified path rather than import its whole module. Works fine with enums, but not with abstract over enum.
  2. destructuring the return value of a function without having the return type imported. Also works fine with enums, fails with abstract over enum.
  3. calling a function that consumes the abstract, without wanting to import anything. Again, works just fine when the argument type is the enum, fails with abstract over enum.

These ☝️

@Simn
Copy link
Member

Simn commented Sep 21, 2018

That's a separate problem, and I think there's still an open issue for this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants