From: Jordi Date: 2007-11-09T01:01:12+09:00 Subject: Re: Proposal of a new operator for Method and Proc On Nov 8, 2007 10:45 PM, Austin Ziegler wrote: > > On 11/8/07, Jordi wrote: > > == The search for events == > > > > First, the classes below are not tested and I'm just a beginner, some > > errors are took for granted. Won't affect my discourse. I wanted to > > implement a timer, looking in google the first thing you get is > > something like this: > > > > class Timer > > > > def start (timeout) > > th = Thread.new do > > loop do > > # work done by the timer > > sleep(timeout) > > end > > end > > end > > end > > > > Let's suppose I'm only using the timer to implement some kind of counter. > > Cool, BUT I would like that the work of the timer be only time > > related! and the counter be in its relevant class. > > Also I may want to reuse this class. > > > > > > So I thought about something like this > > > > class Timer > > > > def initialize (*arguments) > > connections=[] > > @arguments=arguments > > end > > def register_work ( pointer) > > connections += pointer > > end > > def deregister_work (pointer) > > connections -= pointer > > end > > def deregister_all > > connections =[] > > end > > def list > > connections.each { |l| puts "#{l}" } > > end > > > > def start (timeout) > > th = Thread.new do > > loop do > > # work done by the timer > > connections.each { |l| l.call(*@arguments) } > > sleep (timeout) > > end > > end > > end > > > > end > > > > I wrote pointer, I meant for instance Counter.count() > > > > This class is much more general and powerful. > > The work is done in the relevant classes and not inside the timer > > Also, we can register as many connected classes as we want. > > > > > > Seeing that this is Good (tm) I thought about how can I make any class use this. > > Of course I can make a class with the register, deregister and call > > thing and mixin it. > > > > But Wouldn't be could that these encapsulated connections are > > supported natively by ruby? > > > > About benefits of this scheme: > > http://doc.trolltech.com/4.3/signalsandslots.html > > > > About a comparison of implementations (going straight to the > > comparison table is better): > > http://scottcollins.net/articles/a-deeper-look-at-signals-and-slots.html > > > > > > ==The cool stuff == > > > > I thought about how to get this feature implemented without polluting > > the most basic class of Ruby. > > I think that the most powerful and less polluting way is adding a new > > operator to Method and Proc. > > > > > > Example. > > I have a class Page, a class Timer and a class Counter. The Page > > instance is the parent of both instances of Timer and Counter. > > > > class Page > > > > def initialize > > counter = Counter.new > > t=Timer.new > > > > t.timeout -> counter.update > > counter.overrun -> show_error > > > > end > > > > end > > > > > Some notes: > > We connect the timeout of the timer with the update of the counter. > > We connect the overrun of the counter with an error show by the page. > > The Timer whenever it timeouts, it just ... call to its .timeout() method. > > > > Note that the Timer class does not need to be modified to make > > something new (update the counter) . It is a real isolated software > > component. > > The same goes to the counter, it overruns and let the world know about > > it. That is its job. Don't need to know about the Page having and > > error. > > > > I proposed "->" in my ignorance. It may appear as a redirect rather > > than "something you add". Surely something else is better. > > > > > > > > With this scheme, we have a general and easy , cool, even rubyish way > > to connect to events and to connect code arbitrarily to any class > > method! > > For instance, we need to make some clean up each time we call to > > File.close() (whatever reason) > > Previous simple way > > > > def close (file) > > file.close > > @filesOpened -= 1 > > end > > > > new way (in the construstor): > > > > file.close -> { @filesOpened -= 1 } > > > > { @filesOpened -= 1 } will become a Proc called when file.close > > finish. See that the implementation in C should be efficient (a list > > of pointers to functions) > > > > Now suppose that you load a new plugin that also need to do some > > cleanup when the file open by the main class is closed. When loaded it > > will add itself whatever it needs for itself. > > > > > > i feel my examples are not very good... sorry. > > > > > > == Summary == > > > > This addition to the language isolates software components and creates > > a new very powerful construction at a very low cost. > > > > > > > > > > -- > > Jordi Polo���(��������������������������� > > ��������������������������������������� ��������������������� > > ��������������������������� D1 > > > > > > > -- > Austin Ziegler * halostatue@gmail.com * http://www.halostatue.ca/ > * austin@halostatue.ca * http://www.halostatue.ca/feed/ > * austin@zieglers.ca > > > > > On 11/8/07, Jordi wrote: > > On Nov 8, 2007 7:03 PM, Gonzalo Garramu��o wrote: > > > Jordi wrote: > > > > > > > > class Page > > > > > > > > def initialize > > > > counter = Counter.new > > > > t=Timer.new > > > > > > > > t.timeout -> counter.update > > > > counter.overrun -> show_error > > > > > > > > end > > > > > > > > end > > > > > > > > > > You probably want to learn more about ruby blocks, procs and variable > > > scopes. Here's a way to do what you want in a ruby way. > > > > > > class Timer > > > def callback(proc = nil, &block) > > > @proc = proc || block > > > end > > > > > > # protected # but we make it public for this test > > > def timeout > > > @proc.call if @proc > > > end > > > end > > > > > > class Page > > > def show_error > > > end > > > > > > def initialize > > > @counter = 0 > > > t = Timer.new > > > > > > t.callback { @counter += 1 } > > > t.timeout > > > > > > # or... > > > t.callback( method(:show_error) ) > > > t.timeout > > > end > > > end > > > > > > You also probably want to discuss this sort of stuff in ruby-talk until > > > you are more familiar with ruby. > > > > > > > > > As I said I'm not familiar with the syntaxis (my classes may have > > errors) but I am do familiar with the semantics (I think I pretty > > understand "rubyism"). > > > > Your class have a number of problems comparing with my solution. It is > > similar to the second solution I gave. > > > > - You must add that special method callback everytime. Of course you > > can make an special class for that and use mixins ... > > - You can only register one "signal" . My solution can have an > > arbitrary number. > > - You are adding a new method to every class > > > > > > I would like to extend my proposal to disconnection: > > > > t.timeout -> counter.update to connect > > I can't imagine a cool operator to mean disconnect (x is graphically > > perfect though) > > > > > > An alternative proposal, more Qt-like (see reference links in the > > first email) and more verbose: > > > > Method or Proc.connect (Method or Proc) > > Method or Proc.disconnect (Method or Proc) > > > > Or a connect and disconnect in Object but I would really won't like to > > pollute it (Object has "send" method though, it is much like half of > > what I need for the connection). > > > > > > > -- > Austin Ziegler * halostatue@gmail.com * http://www.halostatue.ca/ > * austin@halostatue.ca * http://www.halostatue.ca/feed/ > * austin@zieglers.ca > > > On 11/8/07, Jordi wrote: > > As I said I'm not familiar with the syntaxis (my classes may have > > errors) but I am do familiar with the semantics (I think I pretty > > understand "rubyism"). > > Given your comments, I'm not sure that this is as clear as you think. As > an example, you use "t.timeout" to represent the timeout of the timer, > but there's nothing defined for this. "t.timeout" means sending the > #timeout message to object "t"; it doesn't mean anything to do with some > timeout (or @timeout) variable. I implied more things that I explained, sorry. I try to clarify: timeout is a member of the class Timer t is an instance of that class Adding this mechanism to both Method and Proc will be great. For instance a global Proc could be used in a application for a global sincronization or a global plugin registration. Anyway, adding a new method "connect" and "disconnect" may be clearer. I didn't want to add new methods to the base classes though. In fact, we have send ("method_name") (IMHO should be named receive), it is one third of the equation, the receiving, I only want to add the "emit" and the "connect". emit can be implied. So connect only, trying to minimize it, give me -> . I may minimize too much :P > > Your class have a number of problems comparing with my solution. It is > > similar to the second solution I gave. > > > > - You must add that special method callback everytime. Of course you > > can make an special class for that and use mixins ... > > You can make a mixin that has such registrations, but you then have to > indicate how signals will be raised. > just calling the methods. > > - You can only register one "signal" . My solution can have an > > arbitrary number. > > Implementation detail. You don't have to have a single callback > function; you can use a callback function array. Also think that we have to register for each signal of the calling Object. But I agree, it is trivial to implement it. > > > - You are adding a new method to every class > > Less of a problem than you think. > > Your proposal doesn't actually suggest how these things would *work* -- > how do you implement the slots and signals in general? You need to look > at this a bit deeper and see that there are already analagous things -- > like Gonzalo's example of callback functions -- that can be done without > adding ugly syntax to Ruby. Of course, it can be done. And everything you do with Ruby you can do it with C++, java or whatever. But what I am looking for and this is the important part of this email is A simple, elegant notation for a very powerful and efficient feature! So, people can see a "ruby syntax in 5 minutes page" and say: whoa this language rocks! With this feature I know my code will be much better. I thought that when I saw blocks as methods parameters. I guess I failed to make a "whoa!" syntax for this :P About the details. Do you mean the C implementation of this or how it will be used in Ruby? > > class Timer > def initialize(timeout) > @callbacks = [] > @timeout = timeout > end > def register(cb) > @callbacks << cb > end > def unregister(cb) > @callbacks.delete(cb) > end > def clear_callbacks > @callbacks.clear > end > def start(*args) > th = Thread.new do > loop do > sleep(@timeout) > @callbacks.each { |cb| cb.call(*args) } > end > end > end > end > > class Counter > attr_reader :value > def initialize(start = 0, max = nil) > @value = start.to_i > @callbacks = [] > @max = max > end > def increment > @value += 1 > @callbacks.each { |cb| cb.call } if @value > @max > end > def register(cb) > @callbacks << cb > end > def unregister(cb) > @callbacks.delete(cb) > end > def clear_callbacks > @callbacks.clear > end > end > > class Page > def show_error; end > def initialize > @counter = Counter.new > @timer = Timer.new > > @timer.register(lambda { @counter.update }) > @counter.register(lambda { this.show_error }) > end > end > > Where you call a callback isn't able to be generalized. Also, callbacks for each signal, Timer or Counter may have a lot. It is very possible, but compare that with almost no code if it is built in. That's what I call beautifying a rocking feature :P I see your point and agree, don't need to add it in the sense of Ruby can work without it. > > -austin > -- > Austin Ziegler * halostatue@gmail.com * http://www.halostatue.ca/ > * austin@halostatue.ca * http://www.halostatue.ca/feed/ > * austin@zieglers.ca > -- Jordi Polo