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

allow to include enum #3310

Closed
kostya opened this issue Sep 14, 2016 · 16 comments
Closed

allow to include enum #3310

kostya opened this issue Sep 14, 2016 · 16 comments

Comments

@kostya
Copy link
Contributor

kostya commented Sep 14, 2016

this code not work

enum Errors
  ERROR1 = 1
  ERROR2 = 2
end

include Errors

p ERROR1

need to change enum to module, after that it works, may be allow to include enum also?

@asterite
Copy link
Member

I've thought about this many times, and it's a really nice feature. Maybe we should include this in the standard library:

macro include_constants(type)
  {% for constant in type.resolve.constants %}
    {{constant}} = {{type}}::{{constant}}
  {% end %}
end

enum Errors
  ERROR1 = 1
  ERROR2 = 2
end

include_constants Errors

p ERROR1

:-)

@oprypin
Copy link
Member

oprypin commented Sep 15, 2016

I've also been keeping one of these (and it had to be named like that before the addition of private modules)

@oprypin
Copy link
Member

oprypin commented Sep 15, 2016

It's interesting to consider whether the "included" constants should be documented (in my case they aren't). Maybe a configurable argument?

And now that I think about it, maybe instead it makes more sense to just allow enums to expose their constants unqualified as a language feature, so there's a proper way to indicate that they're available outside without annoying lists of placeholders in documentation.

@ozra
Copy link
Contributor

ozra commented Sep 16, 2016

I'll just throw in a few more or less daunting ideas here:

It could be preferable if we had some "reverse inference" (I know I abuse that term ;-) ) matching literals, as has been discussed earlier, and for this case also unqualified enum-constants, which match towards Enums in ivars type declarations or a parameter's restriction, when explicitly an Enum.

This is harder to do, but cause less name pollution and thus avoids possible conflicts, while still allowing much more clearly readable code.

First, a Naive Suggestion

It can of course never be "fool proof" (just like with freevars contra types) and introduces further rules for order of matching methods:

enum MyEnum
   Red
   Green
   Purple
end

Green = 47

# To show of one problematic
def my-fun(x, y)
  p "my-fun 1: #{x}, #{y}"
end

def my-fun(x, y : MyEnum)
  p "my-fun 2: #{x}, #{y}"
end

my-fun 1, 2  # business as usual
my-fun 1, Red  # Red not found, but a signature exists with enum, try matching in that. Match! Expand.
my-fun 1, Green  # matches the const. Problematic Gotcha!!!

The gotcha alone, is a strong argument against.

So How About Something Similar to Swift?

Indicating a non qualified enum const explicitly:

my-fun 1, 2  # business as usual
my-fun 1, .Red  # Signature exists with enum, try matching them. Match! Expand to qualified.
my-fun 1, .Green  # Of course also gives the expected result.

The dot-const notation could of course prove problematic, unless parenthesis call is demanded when it's used as first arg (that's true already for some literals). However, do we ever access a const-like identifier like that?:

some-fun .Red  # how will the parser take this?
some-fun(.Red)  # this is sure to be ok at least.

And, the ivars of course:

class Foo
  @val : MyEnum = .None
  def do_stuff
    @val = .Purple
  end
end

Another option, is a using declaration which can be used to expose constants in a specific scope only. But using has much more uses [no pun..] so I put it in its' own issue: #3319.

@oprypin
Copy link
Member

oprypin commented Sep 16, 2016

@ozra, I had this idea but with the syntax... :SomeItem. I don't think the dot is viable.

@ozra
Copy link
Contributor

ozra commented Sep 16, 2016

But then it's a symbol!?

@asterite
Copy link
Member

We actually looked at how this works in Swift and like it, and thought about doing something similar in Crystal :-)

Our main issue is that Swift requires type declarations everywhere, while we don't, so in their case if you have a = .Purple they already know what the type of a is, so they can deduce what .Purple means. That, coupled with overloads and other things makes it a bit hard to do, but it's something that we'll try to tackle once we tackle things with more priority.

And like @BlaXpirit says, for enums we though about using symbol literals, as this is also closer to Ruby, though a downside of that is that it adds more magic to the language (and we have to solve the conflict between symbol and enum).

@oprypin
Copy link
Member

oprypin commented Sep 16, 2016

@ozra, it would normally be a symbol. I'm suggesting it only for the contexts where it couldn't possibly be a symbol and it's clear what enum is being referred to.

@ysbaddaden
Copy link
Contributor

for enums we though about using symbol literals

👍 I can't wait for something like this. Compare:

case opcode
when StandardOpcode::NegateStmt
when StandardOpcode::FixedAdvancedPc
end

vs

case opcode
when :negate_stmt
when :fixed_advance_pc
end

Or logger.level = :debug vs logger.level = Logger::Severity::DEBUG

In places where the type is known to be an enum, this would really help increase readability, without sacrificing type safety.

@ozra
Copy link
Contributor

ozra commented Sep 16, 2016

@asterite I suspected you'd already thought about it XD

I think it should try all overloads that match with an Enum in the position, and then try them to see which includes the const.

Of course the problem is that several enums can have the same const. And then a rule is needed for signature precedence. Perhaps whichever enum is declared first or last in the program?

@kostya
Copy link
Contributor Author

kostya commented Sep 16, 2016

with symbols you can typo and program compiled, with enums not.

@ysbaddaden
Copy link
Contributor

ysbaddaden commented Sep 16, 2016

@kostya I assume the symbols would be transformed to their enums at compile time, and thus validated, this failing to compile with a typo.

@bcardiff
Copy link
Member

@ysbaddaden you can do https://play.crystal-lang.org/#/r/19y4 since #285 in order to short case opcode scenario

enum Color
  Red
  Green
  Blue
end

color = Color::Red
a = case color
    when .red?
      1
    when .green?
      2
    when .blue?
      3
    end

pp color
pp a

@RX14
Copy link
Member

RX14 commented Sep 16, 2016

How about not making them symbols? It seems confusing and magic to me. There should be some nonambiguous syntax, or no syntax.

@ozra
Copy link
Contributor

ozra commented Sep 17, 2016

Agree with @RX14 fully. Symbols are symbols. Unqualified Enum consts another.

@jhass
Copy link
Member

jhass commented Jan 13, 2020

We got :symbol shorthand syntax for enum members in calls now. This seems to cover most usecases people had for this. Is there still strong desire for this? Otherwise I'd reject this to keep the language more simple for now.

@RX14 RX14 closed this as completed Jan 13, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants