- Ruby Blocks - Many reasons why we like ruby: - Powerful - Dynamic - Object-oriented - Human-oriented - Garbage-collected - DSLs - ZPET - Poll (unlimitednovelty.com): # Blocks (71%) # Lambdas / Procs (57%) # Mix-ins / Modules (55%) # Open classes / monkeypatching (53%) # Duck-typing (53%) # First-class regexps (49%) # Metaprogramming (49%) # Syntactic sugar e.g. val ||= x; array <<> # Optional parens / English-like readability (44%) # Object#method_missing (42%) - BASIC is easy to learn: 10 X = 1 20 PRINT X 30 X = X + 1 40 IF X <= 10 THEN GOTO 10 - Ruby code is easy to read: (1..10).each do |x| puts x end (but maybe not so easy to write if you don't like punctuation) - Domain-specific Languages task :default => [:test] task :test do ruby "test/unittest.rb" end - Why do we like Ruby? - Blocks! - What is a block? # Method call object.method(parameter) # Method call with block object.method(parameter) do |block_parameter| ... some code ... end - A block is code that can be passed to a method - History of blocks - Lisp - Icon - Smalltalk-80 - Ruby and others - How are blocks used? - Iteration - Resource management - Callbacks - Actors - DSLs - Iteration a = [ 1, 2, 3, 4, 5 ] sum = 0 a.each do |x| sum += x end # side note: you can do the same thing with the inject method - Iteration (cont'd) h = { 'name' => 'Kermit', 'type' => 'frog', 'color' => 'green' } h.each do |key, value| puts "#{key}: #{value}" end - Resource management File.open("file.txt", "w") do |out| out.puts "ALL YOUR BLOCKS ARE BELONG TO RUBY" end - Callbacks server = Rev::TCPServer.new(ADDR, PORT) do |c| c.on_connect { puts "#{remote_addr}:#{remote_port} connected" } c.on_close { puts "#{remote_addr}:#{remote_port} disconnected" } c.on_read { |data| write data } end server.attach(Rev::Loop.default) Rev::Loop.default.run - Actors myactor = Actor.spawn do Actor.receive do |filter| filter.when("dog") { puts "I got a dog!" } end end myactor << "dog" - DSLs Markaby::Builder.new.html do head { title "Boats.com" } body do h1 "Boats.com has great deals" ul do li "$49 for a canoe" li "$39 for a raft" li "$29 for a huge boot that floats and can fit 5 people" end end end - Defining a block - A block is code - A list of statements that form an expression - A block is a closure - It has read/write access to the outer scope - A block is an anonymous method argument - The block parameter may be named or anonymous - Calling a block def foo yield # calls the passed block end foo { ... } foo do ... end - Calling a block (alternate) def foo(&block) block.call() end - block is a Proc - A Proc is a block Object - (A Method is a method Object) - Resource management def synchronize(mutex) mutex.lock() begin yield ensure mutex.unlock() end end - Proc.new { } and lambda() - Blocks are not first-class objects - Procs are first-class objects - Many ways to turn a block into a Proc: - proc { } - lambda { } - Proc.new { } - Do it yourself - Proc from a block def make_proc(&block) return block end a = 0; b = 42 p = make_proc { a + b } a = 10; b = 20 puts p.call #=> 30 - Writing your own DSL - Advanced block usage - Continuations - Coroutines - Blocks in Python One proposed syntax: d = dict( 'foo' : 10, 'bar' : 20 ) using d.each do (key, value): print key, value - Blocks in Python (cont'd) @using(employees.select) def _(employee): if employee.salary > developer.salary: fireEmployee(employee) else: extendContract(employee) - Blocks in Python (cont'd) while some_condition: with flapple() do (arg): if some_other_condition: break # Oops, can't do this! - Blocks in Python (cont'd) @lambda f: do_something(with_our(f)) def result(param): print("called back with "+param) return foobar(param) - Blocks in Python (cont'd) def do_something(func): print("doing something") return func(41)**2 def id(x): return x @id(lambda f: do_something(f)) def result(param): print("called back with", param) return param + 1 print("result is", result, "should be", 42**2) - Blocks in Python (cont'd) - Kinda weird, for now - Just use generators - Blocks in C++ - RAII for resource management - BOOST_FOREACH for iteration - Actors? Coroutines? - libtask? Too straightforward. - boost::coroutine? Too easy. - Use Duff's device! - Protothreads PT_THREAD(display_text(struct state *s)) { PT_BEGIN(&s->pt); if(strlen(s->text) <= LCD_SIZE) { lcd_display_text(s->text); } else { for(s->sptr = s->text; strlen(s->ptr) > LCD_SIZE; ++s->sptr) { lcd_display_text(s->sptr); timer_set(&s->timer, ONE_SECOND); PT_WAIT_UNTIL(&s->pt, timer_expired(&s->timer)); } } PT_END(&s->pt); } - Afterthoughts - My ZPET language has blocks - My ZPET language has no GC - Links * http://www.unlimitednovelty.com/2009/03/what-do-rubyists-like-about-ruby.html * http://www.daniel-azuma.com/blog/view/z3bqa0t01uugg1/implementing_dsl_blocks * http://rev.rubyforge.org * http://doc.revactor.org * http://tav.espians.com/ruby-style-blocks-in-python.html * http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html * http://swtch.com/libtask/ * http://www.sics.se/~adam/pt/index.html