From: jjyruby@gmail.com
To: ruby-core@ruby-lang.org
Subject: [ruby-core:88454] [Ruby trunk Feature#14982] Improve namespace system in ruby to avoiding top-level names chaos
Date: Sun, 12 Aug 2018 09:12:48 +0000 (UTC) [thread overview]
Message-ID: <redmine.journal-73519.20180812091247.1194647146b91d75@ruby-lang.org> (raw)
In-Reply-To: redmine.issue-14982.20180811042606@ruby-lang.org
Issue #14982 has been updated by jjyr (Jinyang Jiang).
I believe namespace can reduce the complexity of organizing codes in large projects.
Recently I write rust code in a large project. Our code base is dependent on other several complex projects. With rust `use` syntax(https://doc.rust-lang.org/book/second-edition/ch07-03-importing-names-with-use.html#bringing-names-into-scope-with-the-use-keyword) we can import the names which we need from other projects without chaotic the global names. It's easy to manage the complexity at the module level.
I can’t image how to handle those complexities under the current ruby requiring system.
I understand the concern, is this a “real problem” or just because we saw the feature in other languages so we want it?
I am currently working on a medium-level ruby project(https://github.com/ciri-ethereum/ciri) and I find it's easy to forget to add some unnecessary requiring.
So in this situation, a user can't directly require this file, He must handle the dependencies manually or require the whole gem.
I do not have real experiences working in millions of lines ruby project, but I believe namespace can help to control complexity, from my other languages experiences.
So I paste my proposal, let the community to discuss the problem whether is real or fake.
-----------------
I realized the essence of the proposal is to allow user to manipulate Binding. Currently, Ruby only has the TOPLEVEL_BINDING, we can't isolated Binding from top-level.
So the proposal essentially requests several primitive to control Binding.
1 isolated requiring: allows to evaluation required files under an isolated Binding(not polluting TOPLEVEL_BINDING).
``` ruby
# requiring into isolated Binding
require ‘foo’, into: :IsolatedBindingModule1
IsolatedBindingModule1.class # Module
IsolatedBindingModule1::Foo # access names
# the old way should still work
# requiring and polluting TOPLEVEL_BINDING
require ‘foo’
Foo
```
2 namespace: allows users to create an isolated Binding scope.
``` ruby
Foo
namespace do # Create an isolated binding.The name maybe not accurate, can be discussed.
Foo # NameError
end
```
3 import: allow user import names from a Binding into another Binding.
``` ruby
Foo
namespace do
# import name from a ruby file
# import primitive is a convenient way to use isolated requiring
import :Foo, from: “foo”
end
```
Then we can extend those methods to more conveniently be used, for example: https://bugs.ruby-lang.org/issues/14982#The-Design
----------------------------------------
Feature #14982: Improve namespace system in ruby to avoiding top-level names chaos
https://bugs.ruby-lang.org/issues/14982#change-73519
* Author: jjyr (Jinyang Jiang)
* Status: Open
* Priority: Normal
* Assignee:
* Target version:
----------------------------------------
## Why
Ruby has evaluation all class/module names in top-level context(aka TOPLEVEL_BINDING).
As a user we basically hard to know how many names in the current context, is causing chaos in some cases. For example:
case 1:
Put common used errors class in a single file, like below
``` ruby
# utils/errors.rb
class FooError
end
class BarError
end
```
In other files under 'utils' we want to use those errors, so the best practice is to use `require_relative 'errors'` in each file we need.
``` ruby
# utils/binary_helper.rb
# we forget require errors
module BinaryHelper
# ...
raise BarError
# ...
end
```
But sometime we may forget to require dependencies in a file, it's hard to notice because
if RubyVM already execute the requires we still can access the name BarError,
but if user directly to require 'utils/binary_helper', he/she will got an NameError.
case 2:
Two gems use same top-level module name, so we can't use them together
## The Reason of The Problem
The reason is we let module author to decision which module user can use. ('require' is basically evaluation, highly dependent on the module author's design)
But we should let users control which names to use and available in context. As many other popular languages dose(Rust, Python..)
I think the solution is basically the same philosophy compares to refinement feature.
## The Design
I propose an improved namespace to Ruby, to solve the problems and still compatible with the current Ruby module system.
``` ruby
class Foo
end
# introduce Kernel#namespace
namespace :Hello do
# avoiding namespace chaos
# Foo -> NameError, can't access TOPLEVEL_BINDING directly
# Kernel#import method, introduce Foo name from TOPLEVEL_BINDING
import :Foo
# in a namespace user can only access imported name
Foo
# import constant to another alias name
# can avoid writing nested module/class names
import :"A::B::C::D", as: :E
# require then import, for convenient
import :"A::B::C::D", as: :E, from: 'some_rb_file'
# import same name from two gems
import :"Foo", as: :Foo_A, from: 'foo_a'
import :"Foo", as: :Foo_B, from: 'foo_b'
# import names in batch
import %i{"A::B::C::D", "AnotherClass"}, from: 'some_rb_file'
# import and alias in batch
import {:"A::B::C::D" => :E, :Foo => Foo2}, from: 'some_rb_file'
class Bar
def xxx
# can access all names in namespace scope
[Foo, Foo_A, Foo_B]
end
end
end
Hello.class # -> module. namespace is just a module
Hello::Bar # so we do not broken current ruby module design
# namespace system is intent to let user to control names in context
# So user can choose use the old require way
require 'hello'
Hello::Bar
# Or user can use namespace system as we do in hello.rb
namespace :Example do
import :"Hello::Bar", as: :Bar
Bar # ok
Foo # name error, cause we do not import Foo in :Example namespace
end
Foo # ok, cause Foo is loaded in TOPLEVEL_BINDING
# define nested namespace
# more clear syntax than “module Example::NestedExample”
namespace :NestedExample, under: Example do
end
namespace :Example2 do
namespace :NestedExample do
end
end
```
Pros:
* Completely compatible with the current module system, a gem user can completely ignore whether a gem is write in Namespace or not.
* User can completely control which names in current context/scope.
* May solve the top module name conflict issue(depends on VM implementation).
* Avoid introducing new keyword and syntax.
* Type hint or name hint can be more accuracy under namespace(not sure).
Cons:
* Need to modify Ruby VM to support the feature.
--
https://bugs.ruby-lang.org/
next prev parent reply other threads:[~2018-08-12 9:12 UTC|newest]
Thread overview: 19+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <redmine.issue-14982.20180811042606@ruby-lang.org>
2018-08-11 4:26 ` [ruby-core:88446] [Ruby trunk Feature#14982] Introduce new namespace system to ruby to avoiding top-level names chaos jjyruby
2018-08-11 12:41 ` [ruby-core:88449] [Ruby trunk Feature#14982] Improve namespace system in " shevegen
2018-08-12 9:12 ` jjyruby [this message]
2018-08-13 1:12 ` [ruby-core:88458] " shyouhei
2018-08-13 3:34 ` [ruby-core:88459] " merch-redmine
2018-08-13 6:40 ` [ruby-core:88461] " jjyruby
2018-08-13 7:05 ` [ruby-core:88462] " jjyruby
2018-08-16 21:02 ` [ruby-core:88506] " jjyruby
2018-08-23 7:25 ` [ruby-core:88612] " ko1
2018-09-12 7:48 ` [ruby-core:88957] " loic.nageleisen
2018-11-22 15:04 ` [ruby-core:89974] " ciconia
2018-11-23 13:38 ` [ruby-core:90006] " v.ondruch
2018-11-23 14:26 ` [ruby-core:90007] " ciconia
2018-11-24 17:04 ` [ruby-core:90041] " v.ondruch
2018-11-24 20:35 ` [ruby-core:90048] " eregontp
2018-11-24 21:05 ` [ruby-core:90050] " v.ondruch
2018-11-24 21:09 ` [ruby-core:90051] " v.ondruch
2018-11-24 21:40 ` [ruby-core:90052] " ciconia
2019-03-23 1:10 ` [ruby-core:91953] " chocolate
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-list from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://www.ruby-lang.org/en/community/mailing-lists/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=redmine.journal-73519.20180812091247.1194647146b91d75@ruby-lang.org \
--to=ruby-core@ruby-lang.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).