Over the past day or so, I’ve been playing around with small bits of Ruby after spending most of the summer in Clojure-land. There’s great similarities in both Ruby and Clojure (functional style, dynamic, etc), and both are very practical choices. Clojure gives you amazing access to JVM land while not forcing you to pull your hair out, and Ruby grants you a good selection of libraries and amazing tools for web development.

But here I want to highlight a few experiments I did to try and brighten up my Ruby-writing experience. The things here could be unneccessarily divergent, but what the hell. It made me realize you can bend Ruby in a lot of ways I hadn’t thought of before

define methods on the fly

def defn name, &b
  Object.send :define_method, name, &b
end

:define_method is an amazing little method that allows you define methods (on any object too, so you could define class/module methods). That’s all there is to it. Now you can do this:

defn(:a) {|x, *y| print x; print y}
# and if you want to superficially lispy
(defn (:a) {|x, *y| print x; print y})

(a 1, 2, 3, 4) #-> 1[2,3,4]

# for multiline methods, this seems easier:
defn (:sum_of_squares) do |x, y|
  x*x + y*y
end

This also works for associating any name with any kind of lambda.

(defn (:foo) {23})
foo == 23 #=> true

let / with

In Lispy languages, there’s something like a function called let which lets you define some names in a new lexical scope and operate with them. An example in Clojure:

(let [x 1] x) ;-> 1
x ;-> error, x is not defined outside the above form

In Ruby, we have something kind of like that called #tap, which Zach Hobson covered here. He suggested a simple way to define #let:

class Object
  def let
    yield self
  end
end

# Now you can do this:

((2).let {|x| puts x+1}) == 3 #=> true

But I think an even more beautiful way to do it is like this:

def with coll, &b
 coll.map &b
end

with [[1,2]] {|x,y| puts x+y} #=> 3

For more on destructuring and everything’s that’s possible there, check out this post.

map, reduce, filter, pipe, call, body

There’s a lot more craziness to explore. I’ll let the code, comments, and examples from my gist explain. This is just bits of code here and there, honestly. Tweet at me to discuss! Also, the gist will be the most up-to-date version of this.

# execute an array of lambdas one after another?

def body *args
  args.each {|e| e.call}
end

(body (lambda {print 1}), (lambda {print 2})) #=> output: 12

# map as a "top-level" function. This maps some lambda to an array,
# passing each element to the lambda to call.
# (also, who doesn't like stabby lambdas?)

def map fun, coll
  coll.to_a.collect {|i| fun.call(i)}
end

(map ->(x){x+1}, [1,2,3]) #=> [2, 3, 4]

# reduce as a "top-level" function

def reduce fun, val=nil, coll
  return coll.to_a.reduce(fun.to_sym) unless val
  return coll.to_a.reduce(val) {|sum, i| sum.send fun.to_sym, i} if val
end

(reduce :+, [1,2,3]) #=> 6
(reduce :+, 3, [1,2,3]) #=> 9


# piping a single input through a bunch of functions
# (yay, composable functions!) (UPDATED in June 2016)

def pipe(value, *methods)
  methods.reduce(value) do |memo, method|
    if method.is_a? Symbol
      send(method, memo)
    elsif method.is_a? Proc
      method.call(memo)
    end
  end
end

# example usage:
hash = {a: 1, b: 2}
(pipe hash, ->(h) {h[:a]}, ->(i) {i.to_f}, :puts)
# or in a more traditional way:
pipe(hash, ->(h) {h[:a]}, ->(i) {i.to_f}, :puts)

# good old filter is also pretty easy to define

def filter pred, coll
  coll.to_a.select &pred
end

(filter ->(x){x.even?}, [1,2,3]) == [2] #=> true

# a function to call a lambda (forcing it to execute)

def call b; b.call; end

saved = lambda {puts "did I get called?"}
#=> #<Proc:blahblahblah (lambda)>

(call (lambda {puts "I got called!"; reduce :+, [1,2,3]}))
#=> output: I got called!\n => 6