分享

eigenclass - Changes in Ruby 1.9

 weicat 2007-03-26
eigenclass logo

Changes in Ruby 1.9

Last major update on 2007-02-07.

I have scanned over 25000 lines of Changelogs to extract the changes between the stable branch and HEAD. These include important syntax additions, lots of modifications in the behavior of Procs and lambdas, new Enumerator and Enumerable goodies, convenient methods to interact with the OS, new IO stuff...

This is not Ruby 2.0!

Keep in mind that Ruby HEAD is being used to try wild and weird ideas. It should by no means be understood as a final say on what Ruby 2.0 will be like. A few decisions are firm and were labelled as Ruby2 in the Changelogs by matz. The below list also includes many crazy ideas (marked as EXPERIMENTAL in the Changelogs and here) which will probably be dropped in short.

Preliminary notes

  • this is, by necessity, work in progress, as Ruby 1.9 keeps evolving; I‘ll try to keep it up-to-date.
  • the snippets which include the resulting value in a comment were evaluated using my XMP filter, under ruby 1.9.0 unless otherwise stated. The 1.8 interpreter used were either ruby 1.8.4 or 1.8.5.

Table of contents



New syntax and semantics

New constant lookup rules

Now constants are looked up in the following order:

  1. current class
  2. super classes except Object
  3. lexically enclosing classes/modules
  4. Object

The new rules entail differences in dynamic constant lookups too:

class A
BAR = 1
def foo(&b); instance_eval(&b) end
end
a = A.new
a.foo { BAR }                                     # => 1

vs. 1.8:

class A
BAR = 1
def foo(&b); instance_eval(&b) end
end
a = A.new
a.foo { BAR }                                     # =>
# ~> -:7: uninitialized constant BAR (NameError)
# ~> 	from -:3:in `foo‘
# ~> 	from -:7

See also ruby-talk:181646.

New literal hash syntax [Ruby2]

{a: "foo"}		# => {:a=>"foo"}

Block local variables [EXPERIMENTAL]

Used as follows:

# {normal args; local variables}
d = 2
a = lambda{|;d| d = 1}
a.call()
d		# => 2

When a variable is shadowed, ruby1.9 issues a warning:

-:2: warning: shadowing outer local variable - d

Block arguments are always local

a = 1
10.times{|a| } # !> shadowing outer local variable - a
a                                                 # => 1

New syntax for lambdas [VERY EXPERIMENTAL]

 a = ->(b,c){ b + c }
a.call(1,2) # => 3

Note that this does not replace the traditional block syntax. Matz has already said the latter is here to stay, forever. The new syntax allows to specify default values for block arguments, since

 {|a,b=1| ... }

is said to be impossible with Ruby‘s current LALR(1) parser, built with bison.

You can use the new syntax without parenthesis for the arguments:

 -> { }.call # => nil
-> a, b  { a + b }.call(1,2) # => 3
c = 1; -> a, b; c  { c = a + b }.call(1,2); c # => 1

It can get very tricky though:

 c = 2; -> ;c { c = 1 }.call; c # => 2

or even

 c = 2; -> *d ; c { d }.call(1,2,3) # => [1, 2, 3]
c = 2; -> ; c { c = 1 }.call; c # => 2

.() and calling Procs without #call/#[] [EXPERIMENTAL]

You can now do:

a = lambda{|*b| b}
a.(1,2)		# => [1, 2]

Note that you need the period:

a = lambda{|*b| b}
a(1,2)	        # =>
#           (eval):2: syntax error...
#                         (a)(1,2)...
  1. ~> -:2: undefined method `a‘ for main:Object (NoMethodError)

You can use any expression inside the parentheses:

(lambda{|a,b| a + b}).(1,2)		# => 3

.() will try to use #call no matter the receiver:

 "foo".(1,2)   # ~>  undefined method `call‘ for "foo":String (NoMethodError)

Block arguments

Blocks can take &block arguments:

define_method(:foo){|&b| b.call(bar)}

ruby-dev:23533

Method used for splat arguments: #to_splat

  1. to_splat is used instead of #to_a.

nil.to_splat returns [].

Multiple splats allowed

As suggested by Audrey Tang, 1.9 allows multiple splat operators when calling a method:

   def foo(*a)
a
end
foo(1, *[2,3], 4, *[5,6])                        # => [1, 2, 3, 4, 5, 6]

Mandatory arguments after optional arguments allowed

(ruby-dev:29014)

   def m(a, b=nil, *c, d)
[a,b,c,d]
end
m(1,2)                                         # => [1, nil, [], 2]

c semantics

a now returns a single character string instead of an integer:

 a   # => "a"

Arguments to #[]

You can use splats, "assocs" (hashes without braces) and block arguments with #[]:

   RUBY_VERSION                                       # => "1.9.0"
RUBY_RELEASE_DATE                                  # => "2006-06-11"
class Foo; def [](*a, &block); block.call(a) end end
a = (0..3).to_a
Foo.new[*a, :op => :+]{|x| x }                     # => [0, 1, 2, 3, {:op=>:+}]

printf-style formatted strings (%)

%c can print a one character String (as returned e.g. by ?c).

Newlines allowed before ternary colon

 p 1 == 2 ?
0
:
1
# >> 1

ruby-dev:29189

Kernel and Object

BasicObject

BasicObject is a top level BlankSlate class:

 BasicObject.instance_methods
# => ["__send__", "funcall", "__id__", "==", "send", "respond_to?", "equal?", "object_id"]
Object.ancestors       # => [Object, Kernel, BasicObject]

#instance_exec

Allows to evaluate a block with a given self, while passing arguments:

def magic(obj); def obj.foo(&block); instance_exec(self, a, b, &block) end end
o = Struct.new(:a,:b).new(1,2)
magic(o)
o.foo{|myself,x,y| x + y }		# => 3

send doesn‘t always call private methods anymore (#__send, #__send!, #funcall)

ruby-talk:153672 It is still possible to call them with the newly introduced #__send! and funcall methods.

class Foo; private; def foo; end; end
Foo.new.funcall(:foo)		# => nil
Foo.new.send(:foo)		# ~> in `BasicObject#send‘: private method `foo‘ called for #<Foo:0xa7d3267c> (NoMethodError)

Note that send(meth) (with no explicit receiver) can still call private methods:

class Klass
def hello(*args)
"Hello " + args.join(‘ ‘)
end
end
k = Klass.new
k.send :hello, "gentle", "readers"   #=> "Hello gentle readers"
send(:puts, "foo")    # prints "foo"
1.send(:puts, "foo")  # NoMethodError exception
  1. >> foo
  2. ~> -:10:in `BasicObject#send‘: private method `puts‘ called for 1:Fixnum (NoMethodError)
  3. ~> from -:10

Kernel#require

The value stored in $" when requiring a file contains the full path, i.e. it works like

$" << File.expand_path(loaded_file)

ruby-dev:26079

Object#=~

Now returns nil instead of false.

 1 =~ 1		# => nil

ruby-core:05391.

Object#tap

Passes the object to the block and returns it (meant to be used for call chaining).

"F".tap{|x| x.upcase!}[0]                       # => "F"
# Note that "F".upcase![0] would fail since upcase! would return nil in this
# case.

Kernel#instance_variable_defined?

a = "foo"
a.instance_variable_defined? :@a                  # => false
a.instance_variable_set(:@a, 1)
a.instance_variable_defined? :@a                  # => true

Kernel#define_singleton_method

a = ""
a.define_singleton_method(:foo){|x| x + 1}
a.__send!(:foo, 2)                                           # => 3

The new singleton method will be private:

a = ""
a.define_singleton_method(:foo){|x| x + 1}
a.foo(2)                                          # ~> private method `foo‘ called for "":String (NoMethodError)

Kernel#singleton_methods, Kernel#methods

They now return an array of symbols (instead of strings):

a = ""
class << a; def foo; end end
a.singleton_methods                               # => [:foo]

Class and Module

Module#instance_methods, #private_instance_methods, #public_instance_methods

They now return an array of symbols (instead of strings):

class X; def foo; end end
X.instance_methods(false)                         # => [:foo]

vs. (1.8.5)

class X; def foo; end end
X.instance_methods(false)                         # => ["foo"]

Module#const_defined?, #const_get and #method_defined?

These methods now accept a flag specifying whether ancestors will be included in the chain, which defaults to true (see ruby-talk:175899):

module A; X = 1; def foo; end end
module B
include A
const_defined? "X"                              # => true
method_defined? :foo                            # => true
method_defined? :foo, false                     # => false
const_get "X"                                   # => 1
end

vs. (1.8)

module A; X = 1; def foo; end end
module B
include A
const_defined? "X"                              # => false
method_defined? :foo                            # => true
const_get "X"                                   # => 1
end

Module#class_variable_defined?

class X; end
X.class_variable_defined? :@@a                    # => false
class X; @@a = 1 end
X.class_variable_defined? :@@a                     # => true

#class_variable_{set,get}

They are public in 1.9, private in 1.8:

class B; self end.class_variable_set(:@@a, "foo")		# => "foo"

Module#attr is an alias of attr_reader

Use

attr :foo=

to create a read/write accessor. (RCR#331)

Class of singleton classes

singleton class inherits Class rather than its object‘s class

class X;end; x=X.new; class << x; self < X; end		# => true

vs. (1.8)

class X;end; x=X.new; class << x; self < X; end		# => nil

[ruby-dev:23690]

Class variables<%

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多