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

Add file-private types. Part of #2950 #3279

Closed
wants to merge 1 commit into from

Conversation

asterite
Copy link
Member

@asterite asterite commented Sep 8, 2016

This makes it possible to use private with top-level types in a file, making them file-private. This is similar to using private on a top-level def.

For example:

# foo.cr
private class Foo
end

Foo.new # OK

# bar.cr
require "./foo.cr"

Foo.new # Error: undefined constant 'Foo'

This can be useful in specs, where we need to define some types only for some specs without fear of having name conflicts, but sometimes we might also need types what we don't want to expose outside a file.

Eventually we can add types private to a type, but for now this is a good start. See #2950

@RX14
Copy link
Member

RX14 commented Sep 8, 2016

I'm a little apprehensive about this pull request because it essentially defines another "scope" that you have to worry about. Crystal is (mostly) an object oriented language, and source files do not fit into the object hierarchy. It's a bit vague, but it doesn't "feel" right. I support private and protected types in the context of being private to the enclosing type, because then it allows you to extract file-private types into their own files without refactoring.

It is my opinion that instead of defining private classes, you should (currently, until private types) simply mark them as undocumented. In the stdlib where namespace pollution becomes an issue, I would just place them off the top level into the Crystal namespace. Plus, then the classes are available for people who really know what they're doing to use.

In short, I think that this issue is adequately solved by other means while being more friendly to refactoring, so doesn't warrant the extra effort to maintain another feature.

@bcardiff
Copy link
Member

bcardiff commented Sep 8, 2016

I want to check that this is not an issue.

public_foo.cr

class Foo
end

private_foo.cr

require "./public_foo"

private class Foo
end

foo = Foo.new # ok, this is the file private Foo.

# But, is there a way to reference the public Foo? Do we want it? 
# ::Foo won't work that well because we might need it to reference the private top level Foo
# from nested classes.

@asterite
Copy link
Member Author

asterite commented Sep 8, 2016

@bcardiff To reference the public Foo you can do ::Foo. With ::Name file-private types are never looked up.

@bcardiff
Copy link
Member

bcardiff commented Sep 8, 2016

private class Foo
end

class Bar
  class Foo
  end
  # Foo means Bar::Foo
  # ::Foo should mean the private foo IMO
  # hence the private ::Foo hides the public ::Foo 
end

I am ok with either this or what you just wrote. I just wanted to expose the scenario.

@asterite
Copy link
Member Author

asterite commented Sep 8, 2016

The scenarios I imagine I'd want to use a private type is when I want to declare a type and use it in that file only. In that case I can't imagine why I'd choose a name that would already conflict with other types I'm using in that file.

@bcardiff
Copy link
Member

bcardiff commented Sep 8, 2016

I agree :-) But i can't help myself

@RX14
Copy link
Member

RX14 commented Sep 8, 2016

Having multiple classes with the same complete namespace, but different visibilities strikes me as totally confusing. If a public class ::Foo exists, then declaring a private ::Foo one should be an error IMO

@asterite
Copy link
Member Author

asterite commented Sep 8, 2016

@RX14 On the contrary, that's the whole point of private classes: I want to define a class in this file, and I don't want it to conflict in the future if someone defines a public one with that name. Giving an error in this case would basically make private types useless.

This is the same reason one uses private defs in a file: I want to declare a method and not letting anyone accidentally redefine it.

@ysbaddaden
Copy link
Contributor

I'm a little puzzled on this feature. I can hardly find any usecase. I always use namespaces, so private types means I would need to create them outside of the namespace, at the top level, which seems wrong.

I would see use cases for protected types inside a namespace that would behave like methods: a compile time error when trying to reference them out of scope (ie. no shadowing).

@0x1eef
Copy link

0x1eef commented Sep 9, 2016

two use cases are described in this thread: #3276 (comment). personally i think it is useful feature to have, but it would be nice if it could be used inside a namespace and behaved similar to private_constant from Ruby in that case.

@ozra
Copy link
Contributor

ozra commented Sep 9, 2016

I'm split on this issue. To me it also seems natural to make private types in a namespace, not at the top level. But then again, I don't have to use it.

@RX14
Copy link
Member

RX14 commented Sep 9, 2016

@asterite, my point was that this feature is useless.

@RX14
Copy link
Member

RX14 commented Sep 9, 2016

To clarify, this feature is confusing if you can define multiple seperate classes with the same name, and useless if you can't.

How are you doing to differentiate between two classes with the same name when debugging? You're also breaking the expectations set by almost every programming language that types have a unique name. It strikes me as a completely nonsensical tradeoff for something that should be - and already is - done using namespacing via modules.

@asterite
Copy link
Member Author

asterite commented Sep 9, 2016

@jazzonmymind Wow, thanks for commenting here! I did not know about Ruby's private_constant and I think that it's exactly what we should do. Ruby semantics are similar to what I described in #2950, and for now (and probably forever) we don't need to worry about protected types, maybe it doesn't make sense.

I'll soon update this PR with that addition. Private constants are very useful because they will make :nodoc: less needed. But I also think that file-private types are useful, for example in specs, and this behaviour would be consistent with file-private defs, so I'd like to introduce both changes at the same time.

@asterite asterite closed this Sep 9, 2016
@asterite
Copy link
Member Author

asterite commented Sep 9, 2016

Closed in favor of #3280

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

Successfully merging this pull request may close these issues.

None yet

6 participants