CSC/ECE 517 Fall 2007/wiki1 2 316: Difference between revisions

From Expertiza_Wiki
Jump to navigation Jump to search
No edit summary
No edit summary
Line 1: Line 1:
== The Question ==
== The Question ==
A callback is code that one wants to be called from within a method after that method has been called. That is, it is a method of the caller that is invoked by the callee. Give an example using a closure in Ruby, and show how this is more elegant than similar code in Smalltalk.
A callback is code that one wants to be called from within a method after that method has been called. That is, it is a method of the caller that is invoked by the callee. Give an example using a closure in Ruby, and show how this is more elegant than similar code in Smalltalk.


== Understanding of the question ==
== Understanding of the question ==
The question requires us to compare callback using a closure code constructed in RUBY with a similar code constructed in Smalltalk and then show how the RUBY code is more elegant.
The question requires us to compare callback using a closure code constructed in RUBY with a similar code constructed in Smalltalk and then show how the RUBY code is more elegant.


== Approach ==
== What is Callback? ==
Suppose we need to associate actions to a particular button so that it does a required function. Eg. In a form if we press CLEAR, it should clear the form and SUBMIT should submit the form. Ruby’s blocks are a convenient way to do this.
 
A function passed as an argument is called a callback, since it is "called back" by the other function.[2] In general a callback is a piece of "your" code you ask "some other piece" of code to invoke in certain events, e.g. some code to be executed if a button is clicked on the screen.
 
In most languages you specify a call back by passing the address of your subroutine to the system you're requesting the callback from.
 
In RUBY callback is implemented using blocks. A nameless block of code may be passed as an argument to a method for implementation of callback.
 
=== Blocks and Closures ===
 
Blocks are basically nameless functions. You can pass a nameless function to another function, and then that function can invoke the passed-in nameless function. For example, a function could perform iteration by passing one item at a time to the nameless function. In Ruby, any method can be called with a block as an implicit argument. Inside the method, you can call the block using the yield keyword with a value. We can create a closure out of a block. A closure is a nameless function. You can pass around a nameless function object, the closure, to another method to customize the behavior of the method. As another example, if you have a sort method to sort an array or list, you can pass a block to define how to compare the elements. This is not iteration. This is not a loop. But it is using blocks. A closure object has code to run, the executable, and state around the code, the scope. So you capture the environment, namely the local variables, in the closure. As a result, you can refer to the local variables inside a closure. Even after the function has returned, and its local scope has been destroyed, the local variables remain in existence as part of the closure object. When no one refers to the closure anymore, it's garbage collected, and the local variables go away.[3]
 
== Functor ==
 
A functor is nothing more than an object that behaves like a function (which is the case in RUBY - everything is an object).[2]
 
== Approach to the Question ==
 
Suppose we need to associate actions to a particular button so that it does a required function.  
Eg. In a form if we press CLEAR, it should clear the form and SUBMIT should submit the form. Ruby’s blocks are a convenient way to do this.
We assume that on pressing the button a callback method, button_pressed, will be invoked. The obvious way of adding functionality to these buttons is to create subclasses of Button(which is the class) and have each subclass implement its own button_pressed method.
We assume that on pressing the button a callback method, button_pressed, will be invoked. The obvious way of adding functionality to these buttons is to create subclasses of Button(which is the class) and have each subclass implement its own button_pressed method.
Eg.  
Eg.  
Line 16: Line 36:
     # do submit actions...
     # do submit actions...
     end
     end
end
end
submit_button = FormButton.new
submit_button = FormButton.new
This will lead to a large number of subclasses. We can fix this problem using blocks in RUBY.
This will lead to a large number of subclasses. We can fix this problem using blocks in RUBY.
pressbutton = PressButton.new
pressbutton = PressButton.new
Line 29: Line 49:
     end
     end
   end
   end
submit_button = FormButton.new("Submit") { pressbutton.submit }
submit_button = FormButton.new("Submit") { pressbutton.submit }
reset_button = FormButton.new("Clear") { pressbutton.reset }
reset_button = FormButton.new("Clear") { pressbutton.reset }
 
The key to all this is the second parameter to FormButton#initialize. If the last parameter in a method definition is prefixed with an ampersand (such as &action), Ruby looks for a code block whenever that method is called. That code block is converted to an object of class Proc and assigned to the parameter. You can then treat the parameter as any other variable. When the callback method button_pressed is invoked, we use the Proc#call method on that object to invoke the block.
 
== Comparisons of Smalltalk and Ruby code ==
 
Smalltalk
 
foo
    | xs |
    xs := #(1 2 3 4).
    xs do: [:x | ^x].
    ^0
  bar
    Transcript show: (self foo) "prints 1"
 
In Smalltalk, ^ will return from method foo, and not just from the closure. do: is defined as a plain method, and there is nothing special about it.
 
foo
    ^[ x: | ^x ]
  bar
    | f |
    f := self foo.
    f value: 123 "error!"
 
When block returned by method foo is invoked, it attempts to return a value from foo. Since the call to foo has already completed, this operation results in an error.
 
Ruby
 
def foo
    f = Proc.new { return "return from foo from inside proc" }
    f.call # control leaves foo here
    return "return from foo"
  end
  def bar
    f = lambda { return "return from lambda" }
    f.call # control does not leave bar here
    return "return from bar"
  end
  puts foo # prints "return from foo from inside proc"
  puts bar # prints "return from bar"
 
It allows the programmer to choose the way he wants "return" to be captured.Both Proc.new and lambda in this example are ways to create a closure.[4]


The key to all this is the second parameter to FormButton#initialize. If the last parameter in a method definition is prefixed with an ampersand (such as &action), Ruby looks for a code block whenever that method is called. That code block is converted to an object of class Proc and assigned to the parameter. You can then treat the parameter as any other variable. When the callback method button_pressed is invoked, we use the
== References ==
Proc#call method on that object to invoke the block.


Now if we want to do the same task in Smalltalk,
1. http://mccammon.ucsd.edu/~oompaa/Oompaa/oompaa/global/doc/callback.html<br>
2. http://forum.java.sun.com/thread.jspa?threadID=485166&messageID=2269118<br>
3. http://www.artima.com/intv/closures.html<br>
4. http://en.wikipedia.org/wiki/Closure_(computer_science)

Revision as of 02:10, 15 September 2007

The Question

A callback is code that one wants to be called from within a method after that method has been called. That is, it is a method of the caller that is invoked by the callee. Give an example using a closure in Ruby, and show how this is more elegant than similar code in Smalltalk.

Understanding of the question

The question requires us to compare callback using a closure code constructed in RUBY with a similar code constructed in Smalltalk and then show how the RUBY code is more elegant.

What is Callback?

A function passed as an argument is called a callback, since it is "called back" by the other function.[2] In general a callback is a piece of "your" code you ask "some other piece" of code to invoke in certain events, e.g. some code to be executed if a button is clicked on the screen.

In most languages you specify a call back by passing the address of your subroutine to the system you're requesting the callback from.

In RUBY callback is implemented using blocks. A nameless block of code may be passed as an argument to a method for implementation of callback.

Blocks and Closures

Blocks are basically nameless functions. You can pass a nameless function to another function, and then that function can invoke the passed-in nameless function. For example, a function could perform iteration by passing one item at a time to the nameless function. In Ruby, any method can be called with a block as an implicit argument. Inside the method, you can call the block using the yield keyword with a value. We can create a closure out of a block. A closure is a nameless function. You can pass around a nameless function object, the closure, to another method to customize the behavior of the method. As another example, if you have a sort method to sort an array or list, you can pass a block to define how to compare the elements. This is not iteration. This is not a loop. But it is using blocks. A closure object has code to run, the executable, and state around the code, the scope. So you capture the environment, namely the local variables, in the closure. As a result, you can refer to the local variables inside a closure. Even after the function has returned, and its local scope has been destroyed, the local variables remain in existence as part of the closure object. When no one refers to the closure anymore, it's garbage collected, and the local variables go away.[3]

Functor

A functor is nothing more than an object that behaves like a function (which is the case in RUBY - everything is an object).[2]

Approach to the Question

Suppose we need to associate actions to a particular button so that it does a required function. Eg. In a form if we press CLEAR, it should clear the form and SUBMIT should submit the form. Ruby’s blocks are a convenient way to do this. We assume that on pressing the button a callback method, button_pressed, will be invoked. The obvious way of adding functionality to these buttons is to create subclasses of Button(which is the class) and have each subclass implement its own button_pressed method. Eg.

class FormButton < Button
    def initialize
        super("Submit") # invoke Button's initialize
       end
   def button_pressed
   # do submit actions...
   end
end
submit_button = FormButton.new

This will lead to a large number of subclasses. We can fix this problem using blocks in RUBY. pressbutton = PressButton.new

  class FormButton < Button
    def initialize(label, &action)
       super(label)
       @action = action
    end
    def button_pressed
      @action.call(self)
    end
 end
submit_button = FormButton.new("Submit") { pressbutton.submit }
reset_button = FormButton.new("Clear") { pressbutton.reset }

The key to all this is the second parameter to FormButton#initialize. If the last parameter in a method definition is prefixed with an ampersand (such as &action), Ruby looks for a code block whenever that method is called. That code block is converted to an object of class Proc and assigned to the parameter. You can then treat the parameter as any other variable. When the callback method button_pressed is invoked, we use the Proc#call method on that object to invoke the block.

Comparisons of Smalltalk and Ruby code

Smalltalk

foo
   | xs |
   xs := #(1 2 3 4).
   xs do: [:x | ^x].
   ^0
 bar
   Transcript show: (self foo) "prints 1"

In Smalltalk, ^ will return from method foo, and not just from the closure. do: is defined as a plain method, and there is nothing special about it.

foo
   ^[ x: | ^x ]
 bar
   | f |
   f := self foo.
   f value: 123 "error!"

When block returned by method foo is invoked, it attempts to return a value from foo. Since the call to foo has already completed, this operation results in an error.

Ruby

def foo
   f = Proc.new { return "return from foo from inside proc" }
   f.call # control leaves foo here
   return "return from foo"
 end

 def bar
   f = lambda { return "return from lambda" }
   f.call # control does not leave bar here
   return "return from bar"
 end

 puts foo # prints "return from foo from inside proc"
 puts bar # prints "return from bar"

It allows the programmer to choose the way he wants "return" to be captured.Both Proc.new and lambda in this example are ways to create a closure.[4]

References

1. http://mccammon.ucsd.edu/~oompaa/Oompaa/oompaa/global/doc/callback.html
2. http://forum.java.sun.com/thread.jspa?threadID=485166&messageID=2269118
3. http://www.artima.com/intv/closures.html
4. http://en.wikipedia.org/wiki/Closure_(computer_science)