Lambdas and blocks

From Reia

Jump to: navigation, search

Contents

Lambdas

Lambdas close over the enclosing lexical scope at the time they are declared, however state mutations inside lambda expressions do not affect the outer scope.

Lambdas return the value of the last expression evaluated:

>> f = fun do
..   10
..   20
..   30
.. end
..
#<Lambda -expr/5-fun-1- (module: erl_eval, arity: 0)>
>> f()
30

Inline lambdas can be declared with a more compact syntax:

>> f = fun { 2 + 2 }
=> #<Lambda -expr/5-fun-1- (module: erl_eval, arity: 0)>

Arguments to funs can be declared with parens after the fun statement. Note that adding arguments changes the arity of the fun:

>> f1 = fun(x, y) { x + y }
=> #<Lambda -expr/5-fun-3- (module: erl_eval, arity: 2)>
>> f2 = fun(x, y, z) do
..   a = x + y
..   a * z
.. end
.. 
=> #<Lambda -expr/5-fun-4- (module: erl_eval, arity: 3)>
>> f2(1,2,3)
=> 9

Blocks

Blocks in Reia are somewhat different from languages like Smalltalk and Ruby. They are semantically equivalent to lambdas. However, rather than being passed as formal arguments, blocks are given a special syntax for the purposes of improving code clarity and syntactic simplicity.

Blocks are passed with the following syntax:

mymethod(arg1, arg2) do |val1, val2|
  ...
end

Blocks can also be enclosed with braces:

mymethod(arg1, arg2) { |val1, val2| ... }

When no parameters are being passed to a method that takes a block, parens may be omitted:

[1,2,3].map { |n| n + 1 }

The block passed to a method can be intercepted as a lambda by placing an & in front of a variable name:

def mymethod(arg1, arg2, &block)
  block(arg1 + 1, arg2 + 2)
end

The & operator can also be used to pass a lambda as the block in a function or method call:

func(arg1, arg2, &block) 

Why blocks?

Why does a lambda need to be disintermediated from other formal parameters?

Block syntax eliminates a lot of syntactic noise associated with passing lambdas as function arguments using the traditional lambda syntax, and furthermore makes it extremely convenient to nest blocks within each other. The Ruby language and tools written within it make extensive use of nested blocks. Two examples of this are the RSpec behavior-driven development DSL and the Builder DSL for generating XML.

Blocks also make it convenient to do quick hacks by chaining several block-based operations together in the interactive interpreter. This is great if you want to make quick deductions about complex state in the program. You're not forced to type a lot of lambda boilerplate and parens to figure out what's going on, and can instead just chain up a lot of operations quickly.

Why can't you just use named functions? Isn't everything clearer that way? Then every transformation automatically has a label for what it's doing.

Named functions make you jump around in the program to understand its logic. Sometimes function labels are enough to infer what is happening, but often you just want to see the code. For more complex operations, code is easier to read than other people's labels. Why should you have to jump back and forth in the program to figure out what's going on?

But beyond that, chances are you're already binding the output of these operations to variables and thus describing the output of already, and in the process providing labels for both the transformation and its output in the form of a named function and the variable bound to its output. The name you bind to the output already provides the clarity of a label describing what the operation is doing. Why do you need to name both the transformation and its output every time? Isn't that tedious, and more information to process? You have what the output is, a hint at what the transformation is doing, and if you want to jump down or up a little bit the actual implementation. Wouldn't just a variable describing the output and the actual implementation of the transformation right there next to each other suffice?

By providing a convenient syntax for the lambda being used for the transformation you get both information about the output and the operation producing it inline and in the logical order of the operations being performed with no jumping back and forth between named functions.

Personal tools