module A
private enum X
A1
A2
A3
end
def self.foo
X.values.each {|z| c = z}
return nil
end
end
A.foo
https://carc.in/#/r/1ufx
Module A declares a private enum, in order to don't open it in public interface (obviously), but instead to use it in the implementation. Expected behavior - module is, well, using it. Current behaviour: things like Enum#values are a macroses, so they don't work with private enum.
Error in line 16: instantiating 'A:Module#foo()'
in line 10: instantiating 'A::X:Class#values()'
in /usr/lib/crystal/enum.cr:329: expanding macro
{% if @type.has_attribute?("Flags") %}
^
in macro 'macro_100019376' /usr/lib/crystal/enum.cr:329, line 2:
1.
> 2. [A::X::A1, A::X::A2, A::X::A3]
3.
private constant A::X::A1 referenced
Of course the public enum can be used instead, it adds only a single symbol to namespace so the issue isn't very serious, but still looks like a bug. Maybe private enums should be just forbidden if there is no easy way to fix it.
It is same problem as #3858. I suggest the solution but @asterite closed this pull request. We wish @asterite to solve it.
I searched issues for "enum"s, so didn't found it. So there is also #3878 where issue with dup was solved, but your solution is more general at the cost of breaking something (I don't quite understand what exactly it is breaking though - macros inside comments?).
The problem is that when macros are expanded they might reference to private types. That is, types that are only accesible to the caller. The expansion somehow lives in other context. Like if it would be an external reference to a private type, hence, it is not visible.
Basically any {{@type}} expand to a syntactic reference to the type where the visibility rules applies.
The following code also fails to compile on private enum
private enum Foo
Bar,
Baz,
Qux
end
Foo.each do |e|
pp e
end
One solution is to remove private types from the language. It was probably a bad idea in the first place.
@asterite is because is a hard problem to solve or because is conceptually flawed?
I find useful been able to decide the surface size that my libraries expose, avoiding me to deal with future breakage and possible deprecation warnings.
Perhaps a more controlled scenario will be able to use protected in that context?
Thank you.
Personally, I always liked the way Python does private stuff: shove an underscore in front. If someone uses it anyway, then they know that it'll probably end up breaking at some point and it's not your fault. However, then you don't really need runtime support behind it.
@luislavena It's because it breaks with methods like Enum.values and such. I think it's better to have that working and simply mark private types with :nodoc:.
Private came handy to manage defs and sample types in specs mainly. Since everything share the namespace at the end of the day. I think those are the main benefits of private. In that sense, I consider acceptable if the private top level methods and types are just prefixed with something the user won't easily type, but when expanded the {{@type}} it would work.
Private came handy to manage defs and sample types in specs mainly
Sounds like a smell: adding features to the language to fix limits in Spec's implementation?
I would say we don't even need this for specs. For example, instead of a file-private free_tcp_port method, there could be a SocketSpec module with a free_tcp_port method, with the benefit of being usable from different spec files.
Most helpful comment
Sounds like a smell: adding features to the language to fix limits in Spec's implementation?
I would say we don't even need this for specs. For example, instead of a file-private
free_tcp_portmethod, there could be aSocketSpecmodule with afree_tcp_portmethod, with the benefit of being usable from different spec files.