Closures are commonly used to abstract over an expression or a statement.
Ruby gives you–not one–but three ways of creating closures from a block: Using Proc.new, the Kernel#proc method, or the Kernel#lambda method. So what’s the difference between these three techniques?
In Ruby 1.81, Kernel#lambda is an alias for Kernel#proc, so they actually behave identically. Proc.new, on the other hand, has slightly different semantics.
stargate$ cat a.rb
def kernel_proc
proc { return "within proc" }.call
return "kernel_proc"
end
def proc_class
Proc.new { return "within Proc.new" }.call
return "proc_class"
end
def kernel_lambda
lambda { return "within lambda" }.call
return "kernel_lambda"
end
if $0 == __FILE__
puts "return from " + kernel_proc
puts "return from " + proc_class
puts "return from " + kernel_lambda
end
With Ruby 1.8:
stargate$ ruby a.rb
return from kernel_proc
return from within Proc.new
return from kernel_lambda
JRuby 1.1.52 also observes the same semantics:
stargate$ jruby a.rb
return from kernel_proc
return from within Proc.new
return from kernel_lambda
Ruby 1.93 has a different story.
In Ruby 1.9, Kernel#proc will behave identically to Proc.new. The reason behind this is described by David A. Black in this post to ruby-talk:
Matz agreed that it was confusing to have
procandProc.newreturn different things, and said he would deprecateproc.
Eigenclass.org also confirms that proc is now a synonym of Proc.new further describing that both receive their arguments with multiple-assignment (block) semantics.
With Ruby 1.9:
proc{|a,b|}.arity # => 2
proc{|a,b| "bacon"}.call(1) # => "bacon"
Proc.new{|a,b|}.arity # => 2
Proc.new{|a,b| "bacon"}.call(1) # => "bacon"
With Ruby 1.8:
proc{|a,b|}.arity # => 2
proc{|a,b| "bits"}.call(1) # => wrong number of arguments
Proc.new{|a,b|}.arity # => 2
Proc.new{|a,b| "bits"}.call(1) # => "bits"
Unfortunately, this change means that Kernel#proc may lead to undesirable side-effects in your code:
With Ruby 1.9
stargate$ ruby1.9 a.rb
return from within proc
return from within Proc.new
return from kernel_lambda
With MacRuby 0.34
stargate$ macruby a.rb
return from within proc
return from within Proc.new
return from kernel_lambda
When in doubt, go with Kernel#lambda. It’s a safe bet.
Updated: Pending further review, I’ve omitted the bit about Proc.new violating Tennent’s Correspondence Principle.
I remember my first passing unit test well.
It was written for a lab assignment for an undergraduate computer science course I took during Spring 2000 at Arizona State University, CSE200, an Introduction to Object-Oriented Programming, or something like that. Back then, though, it wasn’t called a unit test.
CSE200 had close to a hundred students every semester with each student responsible for writing about a dozen lab assignments.
As part of your assignment, you had to write a main function which read input from cin (or System.in) and passed along any arguments to the rest of your program for processing. When your program was done processing, it would write the results to cout (or System.out). To test your lab assignment, the tester (aka the QA TA) would run main and pass in arguments to your assignment. The tester would record your actual output and compare it against the expected output, complaining if there was a difference. There were many factors that went into your assignment’s final grade such as documentation, and your code’s overall object-orientyness, but generally, the more complaints recorded by the tester, the lower your grade.
For fun, I’m told, you could ask the professor to run your assignment against the extra credit data set which basically threw all sorts of data (good and bad) at your program.
Students taking this class were forced to make a decision: either they rose to the challenges of writing code to spec, or they dropped the class before the first lab assignment was due.
I’d be lying if I said that I could sit down at a computer and write a program that ran perfectly, without making a single mistake. Most engineers can’t do this, nor should they.
When writing new code, most of my time is spent exploring ideas and intentionally proving to myself that the computer is doing what I’m telling it to. Programmers like to call this hacking, as in “I have a cool idea, give me a couple of hours to hack something out.”
A programming language geared towards this kind of exploratory hacking always come with an interactive console sometimes referred to as the REPL1. Languages like Ruby, Python, JavaScript, Erlang, Groovy, and Lisp ship out of the box with a REPL, while languages like C++ and the Java programming languages don’t2.
The REPL serves a single purpose: to allow programmers to freely experiment with their ideas both interactively and iteratively. It’s a forgiving environment. It neither cares if you make mistakes nor requires you to write ceremonious amounts of code in order to prove an idea works. A REPL lets you write and verify that your code works, so it often inspires or serves as the early revision of your program.
Have you ever gotten one of those “ah hah” moments when learning something new? I’m sure you know what I’m talking about. They’re the moments that further your domain knowledge on a subject. The moments that build on each other, eventually bridging the distance between novice and master. Frequently using a REPL to learn a programming language causes “ah hah” moments all over the software part of your brain.
You can think of exploratory hacking as the first step to writing solid code.
In the <insert your industry here> industry, the most important thing you need to learn is how to communicate ideas effectively. To a software engineer, this means learning how to communicate ideas with your code which–depending on the collective professional experience of you and your team–ultimately means writing tests which communicate and enforce design.
There’s a ridiculously large body of knowledge on the topic of testing, addressing important questions such as:
My personal philosophy on testing is simple:
We test software to drive communication between team members, to define and enforce the behavior of software, and to share mental assertions against what might one day eventually be an extremely large body of code.
This means that a team sometimes needs to write a lot of tests, and sometimes it doesn’t. It also means you can get away without tests if you’re single person working on a prototype.
At OpenRain, when a new engineer joins our team, before they start building features, they’re assigned the task of writing tests for an existing project. Periodically, this catches them by surprise and the reaction isn’t always positive. I reassure them that this is not the case:
We’re not telling you to write tests because we think you’re a bad programmer. We’re not trying to torture you. We know you’re smart. You’ve got a long and successful track record of building software, you play four instruments, you teach the Argentine Tango, and you can program in six languages. But we didn’t hire you to work for us, we hired you to work with us and in order to do that effectively, you need to know how the software works. The process of writing tests will help you do just that. It will give you a chance to explore the code, to learn how different pieces relate to each other, and to make your own assertions about the way things work.
From a talk I gave at RefreshPhoenix on January 5th, 2009.
From 1999 to 2004, the number of mobile subscribers in Africa jumped from 7.5 to 76.8 million1, an average annual increase of 58 percent. Asia, the next fastest-expanding market, grew by an annual average of 34 percent in that period.
In the first half of 2006, Nokia delivered 153 million mobile devices2 to consumers, out-shipping the entire PC industry by a factor of close to three.
By the end of 2006, there were almost 50 device makers churning out Windows Mobile devices3. The really small and compact ones ones with full QWERTY keyboards.
Apple launched the iPhone on June 29th, 2007 and sold 270,000 iPhones in the first 30 hours4 alone. By the end of Q4 2007, they sold 1,389,000 iPhones5. In 2008, they sold over 10 million6.
Google launched the G1 on October 22nd, 2008. As of this writing, neither Google nor T-Mobile have released any official numbers, but I wouldn’t be surprised if they’ve activated close to a million devices.
Both Apple and Google phones ship with browsers based on Webkit, which is the popular rendering engine used by all the cool kids these days7. According to Android’s roadmap, they’ve added support for an optimized JavaScript engine (Squirrelfish–also used in Safari) and synced up with a Nov 2008 Webkit release, which likely means better support for the -webkit-transition family of properties.
So it looks like your mobile device will run the same stack of web technologies as your personal computer.
This is great news, right? There’s just one small problem.
Just because there are 900 gajillion Internet-powered mobile devices running around, doesn’t mean that people are using it to surf the web, especially the JavaScript-intense, round-corner powered, 2.0 web.
M-Metrics, mobile research firm, released a report on mobile content consumption. 85% of iPhone users browse the web on their phones vs. 58% of Smartphone (Windows, Symbian, Blackberry) vs. 13% of the overall US mobile market. Download.
Why is that? Because designing for the mobile web isn’t just about designing your existing website around form factor constraints. It’s convenient to think that building a mobile website means taking what you have, and making it work on a smaller form factor. This is frustrating for users because they don’t need or want the full blown experience.
Users on mobile devices are moving around. They’re environment is constantly changing. They’re eating at restaurants, walking through customs, working out at the gym, getting a hair cut, bargain hunting, checking up on the latest movie times while their date is in the restroom, or checking up on movie times while in the restroom.
A user behind a personal computer with 24 tabs open on their browser behaves differently than a user on a hot date. Your users are hard real-time systems that need information extracted with surgical precision.
When designing for mobility, design for users on the move. Design for their context. That’s the key to building a successful mobile application.
The W3C has a Mobile web best practice document which suggests that the mobile experience should be specifically designed in mind for your end users.
Cell phone frenzy in Africa, world’s top growth market
↩Apple sold 270,000 iPhones in the first 30 hours
↩Apple shipped 1,119,000 iPhones in Q4 2007
↩Apple officially surpasses 10 million iPhones sold in 2008
↩Apple’s Safari, Google’s Chrome, Adobe’s AIR.
↩
In Ruby, there’s a handy dandy way to get a string representation of a method.
hoktauri$ cat method_to_ruby.rb
def bacon
"chunky"
end
if $0 == __FILE__
require 'rubygems'
require 'ruby2ruby'
require 'parse_tree_extensions'
require 'pp'
pp method(:bacon).to_ruby
end
hoktauri$ ruby method_to_ruby.rb
"def bacon\n \"chunky\"\nend"
A small tribute to Guy Decoux, an early Ruby programmer who once walked the Ruby parse tree to answer a simple operator precedence question posed by Matz.
Read Matz’s question and Guy’s answer. Today, we can answer the same question with the following code:
hoktauri$ cat guy.rb
if $0 == __FILE__
require 'rubygems'
require 'parse_tree'
require 'parse_tree_extensions'
require 'yaml'
pt = ParseTree.new
y pt.parse_tree_for_string("a b c, d")
end
hoktauri$ ruby guy.rb
- - -
- - :fcall
- :a
- - :array
- - :fcall
- :b
- - :array
- - :vcall
- :c
- - :vcall
- :d
Guy passed away earlier this year. His genius was admired and he will be missed by the entire Ruby community.
One more gotcha on operator precedence.
According to the Programming Ruby chapter on expressions, the difference between the && (double ampersand) and and operators, is precedence ordering: && has higher binding than and.
>> false and true ? 'chunky' : 'bacon'
false
>> false && true ? 'chunky' : 'bacon'
"bacon"
>> (false and true) ? 'chunky' : 'bacon'
"bacon"
The or and || (double pipe) operators behave similarly.
These two code snippets also have very different abstract syntax trees. Seeing the difference will also further clarify things.
>> y pt.parse_tree_for_string("false and true ? 'chunky' : 'bacon'")
- - -
- - :and
- - :false
- - :if
- - :true
- - :str
- chunky
- - :str
- bacon
>> y pt.parse_tree_for_string("false && true ? 'chunky' : 'bacon'")
- - -
- - :if
- - :and
- - :false
- - :true
- - :str
- chunky
- - :str
- bacon