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 private types. Fixes #2950 #3280

Merged
merged 1 commit into from
Sep 9, 2016
Merged

Conversation

asterite
Copy link
Member

@asterite asterite commented Sep 9, 2016

This adds private types to the language. #2950 had an RFC for it, but this PR defines the final semantics, which I'll explain now.

Basic example:

class Foo
  # private means: it can be accessed as Bar while inside Foo,
  # can never be accessed as Foo::Bar
  private class Bar
  end

  Bar.new # OK
  Foo::Bar # Error: private constant Foo::Bar referenced
end

Foo::Bar   # Error: private constant Foo::Bar referenced
::Foo::Bar # Error: private constant Foo::Bar referenced

We can start using private types where we previously could only use :nodoc: to only hide them from docs, but they could still be found and used with code. Some places where we could use them: Hash::Entry, the many iterator types, some constants in Base64, and many more places.

One can apply private to all of these: class, module, lib, enum, alias, and constants too:

class Foo
  private ONE = 1

  puts ONE # OK
end

Foo::ONE # Error: private constant Foo::ONE referenced

Like methods, private types can be seen in subclasses. For example:

class Foo
  private class Nested
  end
end

class Bar < Foo
  puts Nested # OK
end

Finally, private types can be marked at the top level: this makes them file-private, just like what using private with methods and macros at the top-level does. For example:

# foo.cr
private class Foo
end

puts Foo.new # OK

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

puts Foo.new # Error

This feature is probably only useful to define types in specs that we don't want to pollute or conflict in the global namespace. For example we could use them here, here, here and many other specs.

I can understand that this last feature (file-private types) might not seem like needed, or strange, but the reason we introduced file-private methods in the first place was to define helper functions in specs without polluting the global namespace, and at least for me they have worked pretty well. I also sometimes use them in the compiler where I just want to define pure functions that don't really belong to any type, but I only need them in that file. For example here.

Private types can of course be returned from methods and used perfectly fine outside the namespace where they are defined. private only makes it impossible to reference them by name outside the namespace. For example this is valid:

class Foo
  private class Bar
  end

  def self.bar
    Bar
  end
end

p Foo.bar.new # => #<Foo::Bar:0x100393fd0>

One can't use protected with types. We could maybe think about it later, but I think just private is enough (for example Ruby has private_constant but no protected_constant).

@asterite asterite added this to the 0.19.1 milestone Sep 9, 2016
@asterite asterite merged commit e1cb4e7 into master Sep 9, 2016
@asterite asterite deleted the feature/file_private_types branch September 9, 2016 14:53
@oprypin
Copy link
Member

oprypin commented Sep 9, 2016

Ohhhhh, it's a miracle. Thank you, thank you, thank you. I've been thinking about how to make a convincing RFC to implement exactly this. Apparently I should've followed the development more closely, because I thought only file-private types were in the works.

@RX14
Copy link
Member

RX14 commented Sep 9, 2016

I still have one concern about this PR. Why is there a difference between Foo::Bar and just Bar when inside Foo? Doesn't it make more sense to always allow full paths to any class, and simply check where you're referencing that from? I suspect that the reason ruby behaves like this is because it's implemented in the Module class which doesn't know where it's being referenced from.

I understand not being able to reference file-private types using ::Foo because they're in a "pseudo-namespace" under ::/path/to/file.cr, but it still feels a little confusing. I still think using namespaces + private modifier is a much nicer solution in almost every way than file-private types but I guess i'll just have to complain to myself.

@oprypin
Copy link
Member

oprypin commented Sep 10, 2016

Why is there a difference between Foo::Bar and just Bar when inside Foo?

@RX14, I'd like to note that this is consistent with the language: private methods can't be called through self (not that I necessarily like this)

@asterite
Copy link
Member Author

@RX14 The algorithm for dealing with private types right now is pretty simple. When we have Foo::Bar::Baz, Foo is searched in the current type and all types (public and private are found). For names after then ::, they are not found if they are private. What you suggest is a big more complex, but we could consider it in the future.

@asoffa asoffa mentioned this pull request May 3, 2017
@chocolateboy chocolateboy mentioned this pull request May 20, 2017
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.

3 participants