Advanced Ruby Studio, Day One

Notes after the jump (as I write them) Review of the day: Morning session was excellent and intense. This afternoon has been theory. As of 3:45pm, it hasn't felt so "advanced". Blocks, Procs, and Closures --------------------------- * "Brace" form of block __always__ associates it self with the nearest method call. Not true of do ... end * break * break takes an optional parameter -- which becomes the return value of the block. * So it's behaving like a typical Proc return value except we're jumping the call stack * Freaky that we're all getting so bent out of shape on like the first slide or two of the real content. ;-) * next * Just return from the block early * Similarly, has an optional parameter that is the return value of the Proc * redo * Me: I've never used it * DT: Used it maybe 3 times in 8 yars of Ruby * Ooooh, I have softcopy of all of the code samples from the class (but almost certainly can't redistribute, sorry) * retry * Ok, didn't know this one... * Like redo except that it __reevaluates the CALL to the proc__ so the below example could be implemented with retry
  i = 0
  loop_until i == 3 do
    i += 1
  end
... because retry will reevaluate the loop\_write call which __reevaluates__ the i < 3 call. __And this is getting removed from Ruby 1.9__ (but will still be available for exception handling) Hrmph. Blocks aren't Procs in MRI unless you pass a & on the param to the method. DT: There are two kinds of Proc objects differentiated by a hidden flag * Using lambda, behaves like an anonymous method * Using Proc.new, get an object that behaves like an inline block * proc changed from an alias to lambda to * Me: Glad that I've always just used lambda * Bindings * Ruby stack frames point to Binding objects * Me: Used in the common Ruby implementations under the cover? * DT: They must * Closures * Whenever a block is creating, it automatically gets a Binding to the current stackframe * Me: So that's how we get closures. Cool. Follows like 1 + 2 = 3 * Blocks need the local Binding in order to be able to implement loop constructs * CF: Multiple calls to binding return different wrappers to the stack frame's Binding -- so they can't be compared at runtime. * Methods are not closures We were given a fun little exercise to write a counter method using a Proc. Below is my Java-ish solution initializing a counter to nil.
def counter(start = 0, increment = 1)
  count = nil
  lambda {
    count += increment if count
    count ||= start
  }
end

c = counter(10, 2)
p c.call
p c.call
p c.call
My neighbor today, Jimmy Baker, tried something that I liked. He passed a added a third parameter on the counter method and used that instead of defining a local variable in the method impl. I thought this was cute because the arguments to the method are also part of the Binding and so could save another line of code. :D Ruby Internals -------------- * Ruby C extensions are running in the same runtime * So ParseTree, for instance, can walk the parse tree * Me: I've read about ParseTree (and Ruby2Ruby). Cool stuff. * Now we're poking a little through Ruby 1.8.6's parse.y (written in yacc) looking at the definition of stmt and compstmt. Uf da! * eval.c * eval_node is how Ruby walks it's internal parsed tree of instructions Design in a Dynamic Language ---------------------------- * DT: ranting about OO programming really being about classes and not objects. * Experienced Ruby programmers prefer composition and rarely use inheritance * Me: At least I'm on the right track * Long argument about whether inheritance hierarchies (and static typing) are all that necessary. * DT: Cites how infrequently Java developers run into ClassCastExceptions when extracting Objects from collections (zero or one times per year) and how this problem was "fixed" with Java 1.5. * CF: Interesting minimalist view of the advantage of composition over inheritance: you only implement the methods that you need and can avoid exposing those that you don't. * DT: Forwardable in std lib * Declarative delegation, i.e., def\_delegator :@member, :member\_method, :exposed\_method is clearer than:
  def exposed_method
    @member.member_method
  end
* Logical delegation via method_missing (simple stuff) * Duck typing * DT gave an example of prototype-based programming in Ruby: defining Objects and having Objects inherit from other Objects by use of clone * CF: Why never to use kind\_of?: because objects can have methods remove at runtime just as easily as they can be added; it's more important to know if an object can respond\_to? a particular message. * Knew this already but I love this explanation. * CF: Points out how the Martin Fowler _Refactoring_ book really turned him on to OO with "Handle Conditions with Polymorphism". With Ruby, just reopen the class, add different impls to each possible object with the same method name, remove the conditional, and just invoke the method outright in the code. * Mixins * Look for "-able" descriptions (i.e., Loggable, Enumerable, Printable, etc.) and sometimes "-ing" * CF on splitting methods up: When I have comments in a method, that's a big hint * Me: Yup. And, wow, do I see a lot of code like that (No, I usually didn't write it!) * Minimize interaction between Mixin and including class -- otherwise coupling the including Class to the Mixin Messin' With Types ------------------ * to_x conversion methods just make a "best guess" * Coercion * Not supposed to be lossy * DT: Representation of one type in another type * Symbol.to_proc * coerce * DT: You'll probably never need it * DT: Good example of double-dispatch "Homework" ---------- * Use coerce to write 1 + "123" to return 124

Posted by evan on Thursday, May 08, 2008

blog comments powered by Disqus