February 27, 2015 by Daniel P. Clark

Value Assignment and Deferment with Lambdas/Procs

When assigning a variable with a raw value the outcome is as you would expect.

greeting = "hello"
# => "hello"
puts greeting
# hello

Now while learning about assigning methods to variables I used to wonder if I was assigning the result value from the method, or simply pointing to the method to be run when the variable was called upon.  To show what happen I will use Time.now for the method being assigned to the value since it changes every second.

x = Time.now
# => 2015-02-27 00:51:04 -0500 
x
# => 2015-02-27 00:51:04 -0500 
x
# => 2015-02-27 00:51:04 -0500 

As you can see the variable x saved the value from Time.now and didn’t call the Time.now method when accessed later.  To show that this is a method being called I’ll show the same done from a block and a method.

y = begin Time.now end
# => 2015-02-27 00:54:19 -0500 
y
# => 2015-02-27 00:54:19 -0500 
y
# => 2015-02-27 00:54:19 -0500 

def the_time; Time.now end
# => :the_time 
z = the_time
# => 2015-02-27 00:55:18 -0500 
z
# => 2015-02-27 00:55:18 -0500 
z
# => 2015-02-27 00:55:18 -0500

As you can see a regular block and method run once when being assigned to a variable and the variable stores the value itself.  Well what if you wanted the variable to always get the value by running the method?

Deferred with Lambdas and Procs

When you assign either a lambda or a proc to a variable then it is available to execute later.  The one thing to note is that you will need to use either .[] or .call methods on the variable to execute the lambda/proc.

a = lambda {Time.now}
# => #<Proc:0x00000002ba0660@(irb):69 (lambda)> 
a[]
# => 2015-02-27 01:01:25 -0500 
a.call
# => 2015-02-27 01:01:29 -0500 
a.call
# => 2015-02-27 01:01:31 -0500 

b = proc {Time.now}
# => #<Proc:0x000000027811b0@(irb):76> 
b[]
# => 2015-02-27 01:04:26 -0500 
b[]
# => 2015-02-27 01:04:27 -0500 
b.call
# => 2015-02-27 01:04:29 -0500

So now the code we want to run when we access the variable runs each time.  Each of these can also be assigned to a constant within a class.

class Foo
  LAMBDA = lambda {Time.now}
  PROC = proc {Time.now}
  STABBYPROC = ->{Time.now}
  BEGINBLOCK = begin Time.now end

  def Foo.the_time
    Time.now
  end
  METHODBLOCK = Foo.the_time
end

Foo::LAMBDA[]
# => 2015-02-27 01:26:11 -0500 
Foo::LAMBDA[]
# => 2015-02-27 01:26:15 -0500 
Foo::PROC[]
# => 2015-02-27 01:26:28 -0500 
Foo::PROC[]
# => 2015-02-27 01:26:28 -0500 
Foo::STABBYPROC[]
# => 2015-02-27 01:26:34 -0500 
Foo::STABBYPROC[]
# => 2015-02-27 01:26:35 -0500 
Foo::BEGINBLOCK
# => 2015-02-27 01:24:27 -0500 
Foo::BEGINBLOCK
# => 2015-02-27 01:24:27 -0500 
Foo::METHODBLOCK
# => 2015-02-27 01:24:27 -0500 
Foo::METHODBLOCK
# => 2015-02-27 01:24:27 -0500

With this you see the same rules apply.  But what I think is really cool is now you can see a class constant also work as a method with lambdas and procs.

Summary

This was a short one.  I only recently came across the ability to have constants be methods you could call.  I really like using lambda in this way.  I’ve decided to use lambda constants for internal methods in my PolyBelongsTo project.  So they are methods available in the project, but their purpose was for internal use.  Methods I’ve intended for more general use are standards methods and easily accessible.  The reason I chose to go about it this way is that it’s a bit unusual to see a constant lambda out in the wild.  So I felt that would be more of an intention indicator.  Yes I know; it defies the very meaning of the word constant.  I expect there may be some people who may be adamant against this kind of use of constants in open source.  You’re more than welcome to share why it’s important to you.  For me, I’m just a Ruby programmer enjoying the beauty and flexibility that Ruby gives me.

If you haven’t been using lambdas and procs yet then I believe that this shows you some of the value they provide.  There are times you need to defer work to be done.

Have fun!  Please feel free to comment, share, subscribe to my RSS Feed, and follow me on twitter @6ftdan!

God Bless!
-Daniel P. Clark

Image by David via the Creative Commons Attribution-NonCommercial-NoDerivs 2.0 Generic License.

#defer#deferred#lambda#proc#programming#ruby

Comments

  1. Keith Bennett
    March 1, 2015 - 5:50 pm

    Nice article. I don’t think there’s anything un-constant about a lambda; the constant refers to the lambda, which does not change, not the value that the lambda produces/returns. Deciding whether a lambda should be assigned to a constant or implemented as just another instance method is an interesting question, though. I do like the constant idea in some cases, e.g. very short (0.5 to 1 liners) code fragments for helper type things such as formatters whose missions are not related to the main theme of the class.

    In some cases, I like using lambdas as nested functions; creating them inside methods and assigning them to a local variable. That way the behavior is defined as narrowly as possible, yet distinguished from the rest of the method. This results in better separation of concerns, high vs. low level code, etc.

    Also, I recommend not using [] to call a Proc unless it performs a service similar to Array#[], Hash#[], etc. I think it’s better to use .() or .call in general. I like .() because I use it a lot and it’s concise.

    Also, I’d recommend renaming STABBY_PROC to STABBY_LAMBDA. Proc is an overloaded term in Ruby unfortunately, used both as a class name and as a type of Proc (where lambda is the other type of Proc). Proc’s defined as -> {} behave like lambdas and not “Procs” (unfortunately used as a term for non-lambda Proc’s).

    More info about lambdas is available at my slideshow at https://speakerdeck.com/keithrbennett/ruby-lambdas-functional-conf-bangalore-oct-2014 and corresponding talk at https://www.youtube.com/watch?v=nGEy-vFJCSE.

    • Daniel P. Clark
      March 1, 2015 - 7:24 pm

      Hi Keith! Thanks for your input! I enjoyed the presentation.

      I haven’t yet used lambdas nested within functions. I was thinking about writing one that did self recursion, but I haven’t found out yet if lambdas can call themselves. I did watch Jim Weirich’s “Y Not- Adventures in Functional Programming” talk a while back and I believe he did something like that so I’m under the impression that I can.

      On the style of usage I understand that ->(x){ puts x } is a more uniform way to follow the way many languages implement procs/lambdas (whatever the case may be). But as ruby methods tend to have a particular style of implementation, e.g. select {|variable| variable.true?} it feels much more Ruby like for me to go with lambda {|variable| variable.true? }. I’m a fan of using pipes to bring in variables into the block.

      But as to you point on not using [] unless it matches the behavior or Array[] or Hash[] I’d like to know more. If you look at the constants I’ve implemented as methods you’ll see mine take parameters: PolyBelongsTo. For the sake of documentation is looks a lot nicer to write MyMethod[param1, param2] than to have to write .call on each method. I’m not stuck on this way, I just thought not having a dot on the method looked nicer. Similar to matching me_method(args) to MyMethod[args] rather than MyMethod.(args) which looks peculiar in my mind. I haven’t been using procs or lambda that long so it may just be me.

      It’s nice to catch you around on the web! Have you been making it to many local meet-ups?

      • Keith Bennett
        March 1, 2015 - 8:59 pm

        Hi, Dan.

        I’ve never tried calling a lambda recursively, but you got me curious. It turns out that it works, I guess because the code in the lambda definition is not executed until the lambda is called, and by that time, the identifier is accessible.

        
        
        f = ->(n) do
          f.(n - 1) if n > 0
          puts n
        end
        
        f.(3)
        
        # Output is:
        1
        2
        3
        

        I was there at Jim’s talk you referred to. Afterwards I told Jim about a guy who came in late, looked at Jim’s code, and said to his neighbor, “What language is that?”. He got a good laugh out of that.

        Regarding { || } vs. ->(), the former resembles a code block, and the latter a method. I personally prefer the method notation, and given that the syntax added to the language in 1.9 is the ->() notation, I assume that it is the suggested form. I asked Matz about it at a Q & A and he said (about its inclusion into the language) “Yeah, I won that one”. So I personally go with the ->().

        Regarding calling a lambda with [], I agree that .call() is overly verbose, especially when one makes wide use of lambdas. But that’s what the .() is for, and I find it more intention-revealing than square brackets, which will raise the question to the reader, is this a lookup or a proc/lambda?

        Regarding technical community, I love it. I gave the lambda talk at DCRUG a couple of weeks ago, and I go all over, even internationally, to attend conferences, unconferences, meetups, etc. I see from your Twitter page that you’re in Winchester, VA. There’s a periodic Loudoun County Ruby lunch, and Arlington RUG is hosting an all-Saturday Ruby unconference soon (see http://arlingtonruby.org). It would be great to meet you sometime.)

        • Daniel P. Clark
          March 2, 2015 - 11:52 pm

          I need to form a community here in the Front Royal and Winchester area. Arlington is such a late and long drive for me (90 minutes). I have been to a couple meetups out that way. The last one was a code review for the water fall puzzle demo. I showed up late soon before you headed out. I also went to the code day event in the Vienna area a year or two ago. You might remember I had a netbook, I didn’t know ‘spec testing at the time, and we hooked up and additional keyboard I had brought to your laptop for a bit of pair programming on a game of life challenge. I believe they shuffled the pair programmers between challenges. But I do look forward to “officially” meeting you sometime soon. I may make it out to an all day event, or a code challenge event, but I usually don’t do pay for admission events. All I have to invest is time at the moment. I definitely want to get more involved in local Ruby community events. I need to find more locals out my way.

          • Keith Bennett
            March 4, 2015 - 12:26 am

            Sorry for not remembering you. I thought you looked familiar. 😉

            Best of luck with the community building. I attended the Harrisonburg RUG a few years ago but I think they closed a year or so ago. There might be some people there looking to build community too.

            Hopefully we’ll see you at Retro Ruby…

Leave a Reply

Your email address will not be published / Required fields are marked *