CSC/ECE 517 Fall 2007/wiki1 2 pk

From Expertiza_Wiki
Jump to navigation Jump to search

Callbacks

A callback is executable code that is passed as an argument to other code. It allows a lower-level software layer to call a subroutine (or function) defined in a higher-level layer. A callback can be used as a simpler alternative to polymorphism and generic programming, in that the exact behavior of a function can be dynamically determined by passing different (yet compatible) function pointers or handles to the lower-level function. This can be a very powerful technique for code reuse.

Normally a callback uses such a parameter as a pointer to application data outside its own scope. This feature is necessary only in a statically scoped language like C or C++ . Dynamically scoped languages (like Ruby and Small Talk) can provide access to application data automatically via closures.

Closures are functions which are evaluated in an environment containing bound variables. They are blocks of code which meets three criteria:

  • They can be passed around as a value and
  • They can be executed on demand
  • They can refer to variables from the context in which it was created

Closures were initally developed in the 1960s in a language called Scheme and later adopted in other languages including Ruby and Smalltalk.

Examples

The code below illustrates a simple case of closure use.

def thrice
    yield #Pass control back to block
    yield
    yield
end


x = 5
puts "value of x before: #{x}"
thrice { x += 1 }
puts "value of x after: #{x}"

The closure(or block) { x+=1 } is passed as a parameter to the function thrice. The value of x before thrice is called is 5. On execution the value becomes 8 i.e. the block {x+=1} was executed thrice.


However, the biggest advantage of closures is that they can refer to the variables which were visible in the lexical environment of the function in which they were defined.

def highPaid(emps)
  threshold = 150
  return emps.select {|e| e.salary > threshold}
end

Closures can also refer to the arguments of the function in which it is defined. For example, conside the ruby code snippet below.

def paidMore(amount)
  return Proc.new {|e| e.salary > amount}
end

#Create new employee
john = Employee.new
john.salary = 200

#Call closure
highPaid = paidMore(150)
print highPaid.call(john)

Note that the closure can refer to variable amount even after the function paidMore is done executing.

Semantic Differences

There are two ways to define a closure in Ruby.

  • Use of Proc
  • Use of lambda

The fundamental difference in the two lies in the way the return from the closure is handled. The following code snippet helps illustrates this.

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"