Books & Tools Techniques

Comprehensive coverage of Ruby 1.8 and 1.9

"The New Most Important Ruby Book"
Peter Cooper,
rubyinside.com

Completely updated for Ajax and Web 2.0

"A must-have reference"
Brendan Eich,
creator of JavaScript

Jude

Jude is my Java documentation browser. It combines Sun's definitive javadocs with the easy-to-use format of Java in a Nutshell, and tops it off with easy keyboard-based navigation and full-text searching.

Jude is available for free evaluation.

See the user's guide for more info

Java in a Nutshell

The 5th edition is now out, with complete coverage of Java 5.0!

It includes a fast-paced tutorial on the language, and a compact quick-reference for the core Java API.

Java Examples in a Nutshell

The 3rd edition, updated for Java 1.4

This edition has all-new coverage of the NIO and JavaSound APIs, completely rewritten Servlets and XML chapters, and coverage of new Java 1.4 features (assertions, logging, preferences, SSL, etc.) added througout. A great book for those who like to learn by example. 193 working examples: 21,900 lines of carefully commented code to learn from.

Java 1.5 Tiger: A Developer's Notebook

Amazon incorrectly credits me as the main author on this book. I'm actually the second author: really more of a consultant. This is a good book about all the language changes in the latest version of Java.

Effective Java

I didn't write this excellent book, but I wish I had.

Author Josh Bloch is probably best known for the collections classes in the java.util package. His experience and wisdom are apparent in this book. I learned from it and recommend it highly.

June 06, 2007

Functional Programming Hacks for Ruby

I've been noodling around with Proc, Method, UnboundMethod and Symbol, and have come up with the following utilities for:

  • function composition
  • function memoization
  • function currying
  • symbol-to-method conversion (different than Symbol.to_proc)

I don't claim that any of this is actually useful. But it might be mind-expanding or character building.

I'm relatively new to Ruby, so comments from more experienced rubyists are more than welcome.

The code follows...

#
# func.rb: experimental functional programming hacks for Ruby
# Copyright (c) 2007 by David Flanagan
# License: http://creativecommons.org/licenses/by/3.0/
#

# This module defines methods and operators for function composition,
# memoization and currying.  It automatically includes itself in Proc and 
# Method.  It is suitable for use with any Proc-like class that 
# responds to []. The methods and operators always return lambdas
# regardless of the type of argument.
module Functional

  # Return a new lambda that computes self[f[args]]
  # Examples, using the * alias for this method
  # 
  # f = lambda {|x| x*x }
  # g = lambda {|x| x+1 }
  # (f*g)[2]   # => 9
  # (g*f)[2]   # => 5
  # 
  # def polar(x,y)
  #   [Math.hypot(y,x), Math.atan2(y,x)]
  # end
  # def cartesian(magnitude, angle)
  #   [magnitude*Math.cos(angle), magnitude*Math.sin(angle)]
  # end
  # p,c = method :polar, method :cartesian
  # (c*p)[3,4]  # => [3,4]
  # 
  def compose(f)
    if self.respond_to?(:arity) && self.arity == 1
      lambda { |*args| self[f[*args]] }
    else
      lambda { |*args| self[*f[*args]] }
    end
  end

  #
  # Return a new lambda that caches the results of this function and 
  # only calls the function when new arguments are supplied.
  # Example: a memoized recursive factorial function
  # 
  # f = lambda {|x|
  #   return 1 if x==0
  #   x*f[x-1];
  # }.memoize
  #
  def memoize
    cache = {}
    lambda { |*args|
      # notice that the hash key is an array of arguments!
      unless cache.has_key?(args)
        cache[args] = self[*args]
      end
      cache[args]
    }
  end

  #
  # Return a lambda equivalent to this one with one or more initial 
  # arguments curried in. When only a single argument
  # is being specified, the >> alias may be simpler to use.
  # Example:
  #   product = lambda {|x,y| x*y}
  #   doubler = lambda >> 2
  #
  def curry_first(*first)
    lambda {|*rest| self[*first.concat(rest)]}
  end

  #
  # Return a lambda equivalent to this one with one or more final arguments
  # curried in.  When only a single argument is being specified,
  # the << alias may be simpler
  # Example:
  #  difference = lambda { |x,y| x-y }
  #  decrement = difference << 1
  #
  def curry_last(*last)
    lambda {|*rest| self[*rest.concat(last)]}
  end

  # Here are operator alternatives for these methods
  alias * compose        # h = f*g
  alias ~ memoize        # cached_f = ~f
  alias >> curry_first   # g = f >> 2 -- set first arg to 2
  alias << curry_last    # g = f << 2 -- set last arg to 2
end

# Add these functional programming methods to Proc and Method classes
class Proc; include Functional; end
class Method; include Functional; end

#
# Add [] and []= operators to the Symbol class for accessing and setting
# singleton methods of objects.  Read : as "method" and [] as "of".
# So :m[o] reads "method m of o".
#
class Symbol
  # Return the method of obj named by this symbol.  This may be a singleton
  # method of obj (such as a class method) or an instance method defined
  # by obj.class or inherited from a superclass.
  # Examples:
  #   creator = :new[Object]  # Class method Object.new
  #   doubler = :*[2]         # * method of 2
  #
  def [](obj)
    obj.method(self)
  end
  
  # Define a singleton method on object o, using Proc or Method f as its body.
  # This symbol is used as the name of the method.
  # Examples:
  #
  #  :singleton[o] = lambda { puts "this is a singleton method of o" }
  #  :class_method[String] = lambda { puts "this is a class method" }
  # 
  # Note that you can't create instance methods this way.  See Module.[]=
  #
  def []=(o,f)
    # We can't use self in the block below, since it is evaluated in the 
    # context of a different object. So we have to assign self to a variable
    sym = self
    # This is the object we define singleton methods on
    eigenclass = (class << o; self end)
    # define_method is private, so we have to use instance_eval to execute it
    eigenclass.instance_eval { define_method(sym, f) }
  end
end

#
# Define [] and []= operators for accessing and setting the instance
# methods of a module.  Note that [] returns an UnboundMethod which
# must be bound to an object before it can be invoked.
#
class Module
  # Access instance methods with array notation.  Returns UnboundMethod
  # Example: String[:reverse].bind("foo").call => "oof"
  alias [] instance_method

  # Define instance methods with array assignment notation
  # Example: String[:backwards] = lambda { reverse }
  def []=(sym, f)
    self.instance_eval { define_method(sym, f) }
  end
end

#
# Use the [] operator to bind unbound methods
#
class UnboundMethod
  # Allow [] as an alternative to bind.  With Module.[] above, we can
  # write code like this: puts String[:reverse]["foo"][]
  # The first brackets get the method, the second bind it, the third call it.
  alias [] bind
end

Advertising
About
Store
Search
Google
Web this site
Archives
Syndicate

Powered by
Movable Type