Blocks In Many Languages

A multilingual example originally from PythonVsRuby

I added some nitpicking comments below, but the largest nitpick is of whether a language even supports "blocks" in the first place.


The statement "lambdas cannot change variables" is meaningless.

A lambda is a form for function creation. If you want to use only lambdas then you can translate from a notation that uses assignment to a notation that uses only lambdas. See LambdaTheUltimateImperative. In SchemeLanguage you could use assignment like this:

  (let ((input (open-input-file "~/test.txt"))
(count 0))
  (let loop ((line (read-line input)))
(if (eof-object? line)
(format "~a lines in test.txt" count)
(begin (set! count (+ count 1))
(loop (read-line input))))))
Or you could translate into lambdas like this:

  (let ((input (open-input-file "~/test.txt")))
  (let loop ((line (read-line input))
(count 0))
(if (eof-object? line)
(format "~a lines in test.txt" count)
(loop (read-line input)
(+ count 1)))))
Where let is just pretty syntax for a lambda:

 (let ((a 1) (b 2)) (+ a b))
=>

 ((lambda (a b) (+ a b)) 1 2)
Generators and iterators also are just syntax for lambdas. The question is why would local assignment be preferred over a functional style of programming? FunctionalProgramming is more modular, and modular is more agile, and more agile is better.

Many people find imperative programs easier to understand, per-line; but that's a side point. The main point is that PythonLanguage, like many other languages, doesn't support FunctionalProgramming very well. For example, it doesn't have TailRecursion, so the above program in Python only works for files under 1000 lines. But even if that weren't the case, it isn't really valid to say, "The limitation in this language construct X doesn't really exist because, by performing a global transformation on your program, you can get an equivalent program in which that limitation doesn't matter."

Here's a PythonLanguage version of the LispLanguage above. It can't be written in terms of Python lambda because Python lambda doesn't allow conditionals.

 infile = file('test.txt')
 def loop(line, count):
  if not line:
 print count, "lines in test.txt"
 else:
 loop(infile.readline(), count + 1)
 loop(line=infile.readline(), count=0)
Though why one would use this instead of (say):
print len(list(file('c:/text.txt'))), "lines in test.txt"
or:
print "%d lines in test.txt" % len(open("test.txt").readlines())
is another matter (Well ok, OneLinersAreEvil? ;-)

It can't be written in terms of Python lambda because Python lambda doesn't allow conditionals. Is that a challenge?

 import sys
 infile = file('test.txt')
 loop = lambda line, count: ( (lambda: sys.stdout.write(str(count) + " lines in text.txt\n")), (lambda: loop(infile.readline(), count+1)) )[bool(line)]()
 loop(infile.readline(), 0)

or:

 import sys

infile = file('test.txt') loop = lambda line, count: ( (line or sys.stdout.write(str(count) + " lines in text.txt\n")) and loop(infile.readline(), count + 1) ) loop(infile.readline(), 0)

or you could go the LambdaCalculus-esque way:

  import sys

def lambdaConditional(b): if b: lambda a b: a() else: lambda a b: b() def loop(line,count): lambdaConditional(not line)(lambda (): print count, "lines in test.txt", lambda (): loop(infile.readLine(),count+1))

although I'm fairly certain that's not "good PythonLanguage" and GuidoVanRossum would crucify me for it. --HarrisonHoughton


Generators and iterators can be implemented in RubyLanguage using BlocksInRuby; there is no need for additional language mechanisms. E.g.:

 def counter_from( n )
proc do
result = n
n = n + 1
result
end
 end

counter = counter_from( 3 ) print counter.call() >>> 3 print counter.call() >>> 4 print counter.call() >>> 5
On the other hand, the PythonLanguage designers chose to add new language mechanisms for generators and iterators, rather than adding a generic mechanism for capturing closures, and then building generators and iterators on top of that.

...and Ruby designer(s?) (YukihiroMatsumoto) chose to add blocks with yield, rather that adding a generic mechanism for calling HigherOrderFunctions...

The Python version is:

 def counter_from(n):
while 1:
yield n
n += 1

counter = counter_from(3).next print counter() print counter() print counter()
''You can do HOF in RubyLanguage as in PythonLanguage, you just need to create Method objects from messages explicitly. Blocks fits a slightly different niche than function composition: they allow intuitive usage of some higher order functions, say:

 open('foo.txt').each do |line|
   puts line if line.length == 10
 end
happily using lambda without need to understand it ''

[I find it interesting that the "cool trick" of using a block to implement a counter is considered a plus for Ruby. In Smalltalk circles, implementations of blocks that reuse their variables are considered defective. I leave it as an exercise to the reader to determine why such implementations of blocks are defective, though you can rest assured that they are.

I consider it proof that RubyIsSmalltalkMinusMinus when Ruby programmers are proud of its defects. Very much like C++ and Java.]

If my understanding of SmalltalkBlocksAndClosures is correct, Smalltalk implementations don't necessarily allocate space for the block variables, so reusing variables in Smalltalk blocks could well result in UndefinedBehavior?


C# supports anonymous methods (blocks) and Icon-style iterators.

The Ruby counter_from routine could be rewritten using anonymous methods:

 Func<int> counter_from( int n ) {
   int result = n;
   return delegate () { return result++; };
 }

Func<T> counter = counter_from( 3 ) Console.WriteLine(counter()) >>> 3 Console.WriteLine(counter()) >>> 4 Console.WriteLine(counter()) >>> 5

or using iterators:

 IEnumerable<int> counter_from( int n ) {
   while (true) 
     yield return n++;
 }

foreach (int n in counter_from(3)) { if (n > 5) break; Console.WriteLine(n) } >>> 3 >>> 4 >>> 5


PhpLanguage

 function counter_from($n)
 {
     $i = $n;
     return function()use(&$i)
     {
         return $i++;
     };
 }

$counter = counter_from(3);

echo $counter,"\n"; echo $counter,"\n"; echo $counter,"\n";

Variables in the outer scope that are to be visible in the inner scope have to be explicitly imported; if the inner scope's modifications of those variables (if any) are to be visible in the enclosing scope (as here where the updated value of $i needs to persist) then the import has to be by reference.


ScalaLanguage

In ScalaLanguage a block is just an expression made up of more than one expression and is normally only evaluated once:

 val v = {
   println("evaluating v");
   3
 }
The above will print "evaluating v" immediately and v will be 3. A function literal is closer to what this page is talking about:
 val v = { () => Int
   println("applying v");
   3
 }
The above will print nothing and v will be a function. Evaluating "v()" will print "applying v" each time and evaluate to 3. To get blocks that get evaluated each time they are used but don't look like functions, ScalaLanguage has call-by-name:
 def f(b: => Int) {
   println(b)
   println(b)
 }

f { println("evaluating block") 3 }
The above will print "evaluating block" and then "3" twice. Using call-by-name and implicit conversions, we can add a ruby-like "times" method to Int:
 class RubyLikeInt?(self: Int) {
   def times(b: => Unit) = (1 to self) foreach (_ => b)
 }
 implicit def rubyLikeInt(i: Int) = new RubyLikeInt?(i)

3 times { println("Hello World!") }
Of course, since a block in ScalaLanguage is just a way to group multiple expressions, the last expression above could be written without the braces:
 3 times println("Hello World!")


CeePlusPlus

C++11 lambdas may capture stack variables by copy or by reference. Sadly, captured references will cause UndefinedBehavior if they escape their parent stack frame. The counter example is implemented as follows:

    std::unique_ptr<std::function<int()>> make_counter(int n = 0) {
      return [=]() mutable { return n++; }
    }

---

GoLanguage

In Go, functions close over the outer scope, and the runtime will make sure to keep closed-over variables alive with the closure:

func Counter(n int) func() int {
x := n
return func() int {
x++
return x
}
}


See BlocksInJava, BlocksInJavaScript, BlocksInObjectiveCee, BlocksInPython, BlocksInRuby, BlocksInCsharp, CoroutinesInDotNet, RubyBlocksVsSmalltalkBlocks


CategoryFunctionalProgramming | CategoryClosure | CategoryComparisons | CategoryInManyProgrammingLanguages


EditText of this page (last edited July 4, 2014) or FindPage with title or text search