Ross Bamford
2/28/2006 1:03:00 PM
On Tue, 2006-02-28 at 20:18 +0900, Mc Osten wrote:
> On Tue, 28 Feb 2006 07:57:19 +0900, Ross Bamford wrote:
>
> > Answering a later question you had, the above singleton method could be
> > defined like:
> >
> > class << test2
> > def size
> > 10
> > end
> > end
>
> Quite interesting. I understand its semantic, but not its syntax. That is
> to say "class" is a keyword. So this is a "new syntax"?
>
'class' here is doing what you expect it to do - defining a class. But
the class that's being defined is a singleton class belonging to the
object on the rhs of the <<. Basically, objects in ruby can have two
classes - the class of which they are an instance, and a singleton class
that relates to (and supplies methods for) just that object. This syntax
is for use with the latter.
> I think that since I can << array the syntax is not *that* new. Still quite
> impressive... well as I said
>
> Every time I do
>
> class << something
> code
> end
>
> I'm evaluating "code" in the "something" context? No.. this is not correct.
> I understand the example, but I'm not able to make abstraction.
>
Not quite. You're actually evaluating it in the context of an instance
of Class, that being the singleton class for 'something'.
Try this:
class << something
p self
end
Since almost everything in Ruby is an expression, you can also do this:
class SomeClass
def singclass
class << self
self
end
end
end
s = SomeClass.new
# => #<SomeClass:0xb7e85d9c>
s.singclass
# => #<Class:#<SomeClass:0xb7e85d9c>>
Notice how the Class returned relates to just that SomeClass instance.
(this for the sake of example, if you were to need it you'd probably
define it elsewhere).
> I also suppose i can
>
> module << something
> code
> end
>
No, singleton modules aren't (AFAIK) supported.
> So I suppose than
>
> class Something
> # code
> end
>
> is "synactic sugar" for
>
> Somtheing = Class::new do
> # code
> end
>
Mostly that's true, but there is at least one difference to keep in mind
- multiple class SomeClass ... definitions will 'reopen' the existing
class and allow new methods to be added:
class TooClz
def a; "a"; end
end
# => nil
TooClz.new.a
# => "a"
class TooClz
def b; "b"; end
end
# => nil
TooClz.new.a
# => "a"
TooClz.new.b
# => "b"
While the assignment here scuppers that (notice Ruby complaining about
the constant being already initialized):
Clz = Class.new { def a; "a"; end }
# => Clz
Clz.new.a
# => "a"
Clz = Class.new { def b; "b"; end }
(irb):24: warning: already initialized constant Clz
# => Clz
Clz.new.a
NoMethodError: undefined method `a' for #<Clz:0xb7e7ad34>
from (irb):26
> > Well, you can't subclass Class. You can do a bit of this kind of thing
> > with Module (a superclass of Class), but bear in mind that class names
> > are just like any other constant in Ruby, so you could do
> >
> > class OtherClass
> > def new
> > "Aha!"
> > end
> > end
> >
> > Foo = OtherClass.new
> >
> > Foo.class
> > # => OtherClass
> >
> > Foo.new
> > # => "Aha!"
> >
> > which is all very confusing, so probably best not to (outside of a few
> > cases you'll find later I guess).
>
> Well it looks like a factory object, since then if I Foo.new.class I get
> (correctly) String.
>
Exactly, which is kind of how I see classes in Ruby, too (especially
since instances can change after instantiation thanks to the singleton
class).
> > Yes, everything in Ruby is an object, at least from the point of view of
> > your Ruby code.
>
> I knew it was an object. I suppose I can say it is an Object.
>
Yes, sorry, I meant both object and Object there :)
> Then the main question: am I supposed to do this sort of things or is
> someone gonna kill me? For example suppose I want to create kind of
> synchronized methods (in the Java sense).
> Syntactically it is quite beautiful to
>
> synchronize :foo
>
> [ and I think in Monitor.synchronize I would alias :foo, define a
> synchronized version of foo that wraps the old :foo ]
>
Yes, you could do something like that:
class Module
def synchronize(sym)
alias_method("__orig_#{sym}", sym)
define_method(sym) do |*args|
puts "Sync"
send("__orig_#{sym}", *args)
puts "Done"
end
end
end
class SomeClass
def syncme(a)
puts a
end
synchronize :syncme
end
SomeClass.new.syncme("hello")
# output:
# Sync
# hello
# done
And no, no-one is going to get upset about it (well, no-one who does
Ruby anyway). I consider this kind of metaprogrammability to be one of
Ruby's sleekest features. Whether your boss, or your customer, or
whoever else, gets upset about it depends on too many external factors
to predict :)
As you mentioned, there are other tools to help with synchronization
(e.g. Mutex) - this is just an example of what you _could_ do.
> > For most purposes you'll want to define on Module instead, which allows
> > your method in both class and module definition contexts.
>
> I never thought to modules that way. I'm quite used to Python, where
> namespaces are authomatically managed using files. Good.
>
> > In Ruby Mammal would have to be a module, and I'd say
> > self.kind_of?(Mammal) :-)
>
> Why it would be a Module? In fact I could have functions and methods that
> manipulate mammals and I may want to have the forementioned Donkey class to
> subclass it.
> It's probably a question of Ruby style I've not yet encountered.
>
The more I thought about this, the more I imagined an enormous class
hierarchy representing the natural world, so I decided to stop thinking
about it in the end :)
> > As you saw, a lot of stuff in Ruby is done with blocks. You can attach
> > blocks to method calls, and one of the ways this is done is by declaring
> > the final argument with a leading ampersand.
>
> Yes. And I do like them a lot. It's one of those things you say "how have I
> done _before_?".
>
> But it does work even without...
>
> def blrun(a)
> puts a
> yield
> end
>
> def blrun2(a, &b)
> puts a
> yield b
> end
>
>
> blrun("a") { puts "b" }
> blrun2("a") { puts "b" }
>
> I suppose it is useful if you want to "give a name" to the block. Am I
> correct?
'yield' is usually the way I'd recommend you go, but yes, sometimes you
need to do something with the block other than call it right away.
The & can also be used with block methods to allow an existing Proc to
be used as the method's block, e.g.
blk = lambda { puts "b" }
blrun("a", &blk)
This comes in handy too when you want to pass a block attached to one
method to another method.
(Aside, the 'yield b' in your second example above will actually pass
the block as an argument to itself).
> > Well, in Ruby functions are methods :).
>
> Whose methods are them? I suppose it should be Object or maybe Kernel
> (which in fact should be the same since Object includes Kernel, right?)
>
> But I tried to define a method and call it with Object.method and gives
> error...
>
Top-level 'functions' are private methods on Object (I was sure it was
Kernel but it doesn't appear so):
def amethod
"Hey!"
end
p Object.private_instance_methods.include?('amethod')
# => true
Object.new.send(:amethod)
# => "Hey!"
Since they're private, and on Object, you get the expected behaviour
that you can only call them with no receiver, and from anywhere.
--
Ross Bamford - rosco@roscopeco.REMOVE.co.uk