Some useful closures, in Ruby
Reginald Braithwaite has just posted a short introduction to closures in Ruby. Closures allow you to pass functions around your program, and build new functions from old ones.
Programming languages that support closures include Perl, Ruby, Python (sorta), Lisp, Haskell, Dylan, Javascript and many others.
The Dylan programming language included four very useful functions built using closures: complement, conjoin, disjoin and compose. The names are a bit obscure, but they can each be written in a few lines of Ruby.
Let’s start with complement:
# Builds a function that returns true
# when 'f' returns false, and vice versa.
def complement f
lambda {|*args| not f.call(*args) }
end
We can use this to build the “opposite” of a function:
is_even = lambda {|n| n % 2 == 0 }
is_odd = complement(is_even)
is_odd.call(1) # true
is_odd.call(2) # false
compose is another useful function:
# Builds a function which calls 'f' with
# the return value of 'g'.
def compose f, g
lambda {|*args| f.call(g.call(*args)) }
end
We can use this to pass the output of one function to the input of another:
mult2 = lambda {|n| n*2 }
add1 = lambda {|n| n+1 }
mult2_add1 = compose(add1, mult2)
mult2_add1.call(3) # 7
The conjoin function is a bit more complicated, but still very useful:
# Builds a function which returns true
# whenever _every_ function in 'predicates'
# returns true.
def conjoin *predicates
base = lambda {|*args| true }
predicates.inject(base) do |built, pred|
lambda do |*args|
built.call(*args) && pred.call(*args)
end
end
end
We can use it to construct the logical “and” of a list of functions:
is_number = lambda {|n| n.kind_of?(Numeric) }
is_even_number = conjoin(is_number, is_even)
is_even_number.call("a") # false
is_even_number.call(1) # false
is_even_number.call(2) # true
The opposite of conjoin is disjoin:
# Builds a function which returns true
# whenever _any_ function in 'predicates'
# returns true.
def disjoin *predicates
base = lambda {|*args| false }
predicates.inject(base) do |built, pred|
lambda do |*args|
built.call(*args) || pred.call(*args)
end
end
end
This allows us to construct the logical “or” of a list of functions:
is_string = lambda {|n| n.kind_of?(String) }
is_string_or_number =
disjoin(is_string, is_number)
is_string_or_number.call("a") # true
is_string_or_number.call(1) # true
is_string_or_number.call(:a) # false
These were four of the first closure-related functions I ever used, and they’re still favorites today.
Feel free to post versions in other languages below!
Want to contact me about this article? Or if you're looking for something else to read, here's a list of popular posts.
These are also pretty easy to achieve in Python, as well.
The lack of Ruby’s nice, polite anonymous blocks in Python can be really annoying at times, but it’s not that difficult to work around their absence for cases like these. Note that you can shorten up
conjoinanddisjoina bit if you are willing to throw out the return values that generated the exit case and just throw back True or False.Of course, Haskell makes these all very terse:
complement = (not .)
compose = (.)
conjoin = foldr (\f g x → f x && g x)
(const True)
disjoin = foldr (\f g x → f x || g x)
(const False)
In two cases, the Haskell implementation is shorter than the Dylan name!
Here’s some sample code:
complement even 1 - True
compose (+1) (*2) 3 - 7
conjoin [(>=1),(<=10)] 1 — True
conjoin [(>=1),(<=10)] 11 - False
disjoin [(<1),(>10)] 11 - True
Do you know of any languages which can make these examples even shorter?
These handy tools are independent of the idea of closures. The functional composition works for any higher-order function regardless of whether the function definitions close on their environment.
Also, seems like your composition primitives would lose any block attachments when called. Might what to use
*args,&blkevery time you accept and propagate the arguments. For compose you’ll need@Eric Kidd:
Haskell’s ‘conjoin’ and ‘disjoin’ can be written even more concisely:
import Control.Monad(liftM2) conjoin = foldr (liftM2 (&&)) (const True) disjoin = foldr (liftM2 (||)) (const False)nmessenger: Interesting! That appears to rely on “Monad ((→) r)”. Here’s the source:
instance Functor ((→) r) where
fmap = (.)
instance Monad ((→) r) where
return = const
f >>= k = \ r → k (f r) r
Frankly, I’m impressed. That’s a monad with some serious potential for abuse.
http://www.lisp.org/HyperSpec/Body/fun_complement.html
http://www.lisp.org/HyperSpec/Body/fun_everycm_s_erycm_notany.html
:-)
nablaone: Yeah, those are also very useful. In Haskell, you could use:
any even 1 # False
any even [1,2] # True
all even [1,2] # False
all even [2,4] # True
And in Ruby:
? {|n| n%20 } [2,4].any? {|n| n%20 }
Of course, it’s possible to write both of these higher-order functions without using closures.
For conjoin and disjoin in ruby I prefer:
predicates.all? {|f| f.call(2)} predicates.any? {|f| f.call(2)}Peter Burns: Thanks! That’s a lot nicer.
Abuse, elegance, what’s the difference? :P
Though it occurs to me that they could be even more transparent.
conjoin fs x = and (map ($ x) fs) disjoin fs x = or (map ($ x) fs)Some transformations and we have:
conjoin = flip (all . flip id) disjoin = flip (any . flip id)I better stop now, golfing is addictive. :)
nmessenger: The map-based versions are nice (if you know what $ does), but the ones with flip are just wrong.
Update: nmessenger just sent me the shortest and most elegant version yet:
conjoin fs x = all ($ x) fs
disjoin fs x = any ($ x) fs
Very nice writeup. Regarding complement for Ruby, I would prefer this, though:
def complement &b
lambda {|args| not b.call(args)}
end
Then you can skip the first reification:
is_odd = complement {|n| n % 2 == 0}
Of course, there’s also APL :
is_odd{assign}2{jot}|
is_even{assign}~{jot}is_odd
Where {jot} is the ‘compose’ operator, used in the first example to curry the ‘modulus’ primitive, in the second to compose the ‘not’ primitive and the function.
Here we use a series of ‘compose’ to obtain the function :
mult2_plus1{assign}(1{jot}+){jot}(2{jot}{multiply})
Conjoin and disjoin are easily translated with {and} and {or} primitives and the {reduce} operator.
{and}/1 0 1
=> 0
{or}/1 0 1
=> 1
With the added bonus that all primitives map their arguments :
2 | 1 2 3 NB. 2 modulus 1 2 3
=> 1 0 1
We define the operator conjoin :
r{assign}(f1 conjoin f2) args
r{assign}^/(f1 args)(f2 args)
Then use it :
is_even conjoin is_number 1 2 3
=> 0 1 0
It still surprises me how “Haskell Golf” is still considered a mark of high intelligence or nerd-prowess, while golf in any other language is just a game…
Well, that’s the fun thing about Haskell golf1—it quickly degenerates into higher mathematics. “You did what? How is that a monad?” And suddenly you’re hip deep in category theory.
Provided the golf is educational, I’m strongly in favor of it.
So everyone, please feel free to golf on this blog. Teach me some cool tricks and tighten up my code. :-)
1Golf: Attempting write a program in the smallest number of keystrokes.
I guess every language’s golf reflects that language’s nature. One of the most interesting golf entries I’ve seen is this one by Ton Hospel
http://groups.google.com/group/pl.comp.lang.perl/msg/3d47c84a1307e381?hl=en
which to me reflects Perl’s focus on results over elegance and its appreciation of cleverness.
Thanks! That’s pretty neat:
> But in “random” mappings with a very small result set like this, the shortest solution is often to make up some magic formula that has no particular meaning, but just happens to give the wanted result.
The way he applies a “magic” function and strikes out irrelevant digits of the output reminds me of the prime game.
With the any and all functions in Python 2.5, the conjoin and disjoin functions become a lot simpler:
It doesn’t quite match the pretty point-free style though.
Eric Kidd: Depending on how you count a Factor version is even shorter.
: complement ( quot — quot ) \ not add ;
: compose ( quot1 quot2 — quot ) append ;
: conjoin ( seq quot — ? )
\ and add t swap reduce >boolean ;
: disjoin ( seq quot — ? )
\ or add f swap reduce >boolean ;
I like compose better as a member of the Proc class, if only because I find using ‘*’ much more clear
Something similar (albeit a little more complicated) could be done for
conjoinand&, so you could haveBut it would be too much work when you can use Enumerable#all? :)