📜 ⬆️ ⬇️

RubyConf 2010: the present and the future of Ruby (I)

I suggest you read the translation of the second part of Ruby’s author’s report, Yukihiro Matsumoto, on RubyConf 2010, published in his blog in Japanese. The first part is a brief historical overview of previous RubyConf conferences and is of no interest to the publication.

For assistance in translating from Japanese, many thanks to Vladimir Sadovnikov .

Future Ruby: Ruby 2.0


This time I will introduce several new features that will be in Ruby:

I am quite sure that so far this has not been the case. The difference is that:

1 Comment Lane: for non-trivial technical terms, I will give an English name in brackets and a link to Wikipedia.

Types

Definition of types:
Description is a collection of methods used as “a simple conceptual model for structuring object-oriented programs.”
from wikipedia (eng.)

It looks almost the same as the module. In essence, the new language element - type - is a subspecies of the module.
')
However, in order to present the functionality of a module in a different way, types implement impurities (mixins) using a method other than including (other modules — here and hereinafter in square brackets). trans.]. Since this method is more flexible, the possibility of including modules has been removed completely.

Problems with the inclusion are:

When inclusion is used to simulate multiple inheritance, some difficult to predict situations arise.

One of them is that the inclusion of a module containing a method that is already defined in the current context may have completely different goals and consequences: [intentional] overlapping an already defined method, random coincidence (name conflict), or both are actually required method, and their names must be unique.

Another problem is that the order of inheritance of modules and, accordingly, the sequence of finding methods when matching names can often be counterintuitive.

module American attr_accessor :address end module Japanese attr_accessor :address end class JapaneseAmerican include American include Japanese end JapaneseAmerican.new.address # which address? p JapaneseAmerican.ancestors # => [JapaneseAmerican, Japanese, American, Object, Kernel] 

In this example, the address attribute method is contained in both the American and Japanese modules, overlapping at the request of the developer or accidentally due to their lack of knowledge of the language. As a result, the methods will be called simply in the sequence corresponding to the order of inheritance.

A “well-trained” Ruby programmer [Rubyist], looking at this code, will of course understand that the address method from the Japanese module will be called.

Currently, Ruby [MRI 1.9] uses the paradigm “the module should not be inherited twice, even if the parent class already includes this module” (author's note: it looks like MacRuby did the same. In 1.9 there was an attempt to change this, but for YARV work requires that the module does not appear among the ancestors twice.). So if you include a module in a class whose parent class already includes this module, it will not appear among ancestors where you expect it.

In addition, since the inclusion of modules adds to the ancestors of the current class all the modules included in this module, and after that they can no longer be included in a place specific to this class. In the end, the inclusion of modules makes the order of inheritance much more confusing.

These problems (in part) are solved by the method of mixing (mix).

Why mixing? Because:

Thus, mixing is implemented as follows:

For example, in this code there are methods with the same name, and therefore mixing will not work:
 module American attr_accessor :address end module Japanese attr_accessor :address end class JapaneseAmerican mix American mix Japanese # => address conflict! end 

In order for the mix to work, you need to explicitly resolve name conflicts:
 class JapaneseAmerican mix American, :address => :us_address mix Japanese, :address => :jp_address end 

Now the methods that were called the same got different names.

You may ask why, instead of complicating the language, not to add the ability to pass the parameters of the inclusion to the include method? Because the shorter name of the method means that it is preferable to use. (comment re: that is why all introspective methods that can potentially make code difficult to understand have detailed names like “instance_variable_defined?”).

Nakada [probably, Nobuyoshi Nakada, one of the Ruby core developers] has already started developing the types needed to implement the mix method at the time of announcing this feature on RubyKaigi [Japanese Ruby Conference], so the patch is ready.

However, various presentations do not address the following problems, which still need to be resolved:

Combinations of methods

On RubyKaigi, mixing was part of a story about method combinations, but later it turned out that one of the parts of the story, related to the ability to “wrap” the method with calls to other methods, turned out to be the most non-trivial, and I singled out it separately.

The proposal is to introduce the prepend method. I already hear the question "Do we already have include and mix, what, is another method now needed?", And I believe that it is really necessary.

This method allows you to add another method "before" the current class to add any functions.
 module Foo def foo p :before super p :after end end class Bar def foo p :foo end prepend Foo end Bar.new.foo # :before, :foo, :after 

If the foo method is defined in the Foo module, and it is also defined in the current class (or module), then prepend wraps the foo method call of the current class using the module's foo method.

The idea of ​​the prepend method comes from Yehuda Katz, who is also a Rails developer; he thinks it would be great to replace them with alias_method_chain in Rails, and I agree with him.

There is no concrete implementation of this possibility yet, but the easiest way to add the T_ICLASS object to the inheritance chain. From this it is possible to begin.

Named Arguments

People tend to forget the purpose of the arguments, and especially the optional ones. For example, the public_instance_methods method takes an optional argument, which means “if false is returned, then return the methods and parent class too,” or vice versa, I always forget it. (Actually, true.)

For example,
 aClass.public_instance_methods (include_super: false) 

So much easier to remember.

In Ruby 1.9, named arguments are simply the last argument with the hash as the default value, and the grammar extension with which this hash is initialized. (comment perev .: few people know that in 1.9 you can make a method like def a(opts={}); end and call it as a(go: true, what: "string") .

New feature 2.0 is a simple way to specify such an entry when defining a method. For example:

Call:
 1.step (by: 2, to: 20) do |i| pi end 

Called method:
 def step (by: step, to: limit) ... end 

After the [community] has accepted the call with the hash, the addition of the named arguments will, in effect, be a slight change.

Namespaces

If you are interested in technical details, you should take a look at the Shugo Maeda presentation , which was also on RubyConf.

Ruby classes are public. This means that at any time you can add a method to an existing class. This class change technique is called “monkey patching”.

It seems to me that this term was generated by the chain “guerilla patching” [guerilla - partisan] -> “patching gorilla” -> “monkey patching”. Of course, if in a language like Ruby, changing classes is easy, then I’m sure that this feature should be provided. That is why the DHH report [David Heinemeier Hanson, author of Rails] "The future without monkey patching" I call the "Future without freedom of change." [here was a quote from Mel Gibson, but my brain parser did not master it].

On the one hand, freedom is good, but on the other, it can have too much influence. Of course, you can override the addition for integers so that 1 + 2 is equal to 42, but most of the programs will stop working due to side effects.

The problem is that all such changes act globally, on the entire program. By placing changes in a closed scope, you can get a convenient and secure method of “free changes” [freedom of patching].

Such a “scope” could be the previously proposed Selector Namespace Classbox; This time, the Selector Namespace, implemented by Maeda’s colleague, can be called a “Refinement”.

For example, here is the program:
 class Integer def / (other) return quo (other) end end p 1 / 2 # => (1 / 2) 

Such a redefinition of the division operator (/) in order to return the result in the form of a rational fraction (note: in this case, an object of the Rational class is returned) is part of the standard mathn library. However, code that expects integer division to also result in an integer may fail due to such a change.

In this study, it is proposed to introduce clarifications (the name may change). A grammar might look like this:
 module MathN refine Integer do def / (other) return quo (other) end end p 1 / 2 # => (1 / 2) end p 1 / 2 # => 0 

Refinement is used as part of the module. Module. This is a great achievement.

Within the module, existing classes can be refined, and such changes will be visible only within the namespace. Thus, inside the module MathN 1/2 returns Rational (1/2), and outside the module the usual quotient.

The difference between the clarifications and the classbox is that, despite the fact that the lexical scope has already ended, the clarification is still in action; for the same reason, many programming languages ​​use dynamic scope. Of course, the clarifications should work imperatively, not lexically.

The namespace is a module using the using method:
 module Rationalize using MathN p 1 / 2 # => (1 / 2) end p 1 / 2 # => 0 

In the Rationalize module, the update defined in MathN is also available.

In addition, if a method is meaningless to define as a class method, then it can be defined inside another method. Within the method, this will work in the same way as the clarification: this way the method can be made completely closed. (Note: In Ruby 1.9, the method defined in this way becomes a member method of the class, that is, nesting has no meaning.)
 class Foo def foo def bar ... end bar # works end def quux bar # does not work end end 

This change that Maeda is working on is quite large and complex; however, the changes are quickly integrated into the main trunk [trunk], but for now, you can applaud your Maeda colleague for this great patch.

PS This translation was far from simple, and I know that many phrases will drive any cryptolinguist crazy, but my conscience did not allow me to pull out from the text only those arbitrary parts that were the easiest to translate. I hope that most of the interesting things will be clear from the few surviving comments and the source code. And I promise that the translation of the next part will be much clearer.

Source: https://habr.com/ru/post/113635/


All Articles