Blocks ****** So far, we've only seen examples of very straightforward Cf0x10 programs, which execute one line after another except for a ``comefrom`` jump. At this point, you might be thinking, "Meh, I could do that in Intercal," so it is time to see where Cf0x10 separates itself the rest: it allows structured programming with lexical scope! That's right, it really is a language for the twentieth century. Like all the good modern languages, Cf0x10 uses syntactically significant indentation. That is, indentation creates a block: +------------------------------------------------+------------------------------------------------+ | Program | Output | +================================================+================================================+ | :: | .. code-block:: none | | | | | leave_this_place | Hello, world | | "Goodbye, cruel world" | | | | | | "Hello, world" | | | | | +------------------------------------------------+------------------------------------------------+ In the above, execution begins in the top-level scope. Nothing ever jumps into the block named ``leave_this_place``, so none of the code in that block executes. For the moment, you can think of blocks as coroutines in other languages, but that's not really accurate, as we'll soon see. Qualified comefrom ================== First, how to jump into and between blocks? In the previous part, we saw "conditional" comefrom, now we see "qualified" comefrom: +------------------------------------------------+------------------------------------------------+ | Program | Output | +================================================+================================================+ | :: | .. code-block:: none | | | | | foo | 1 | | 1 | 2 | | | 3 | | comefrom bar | 4 | | 3 | | | | | | bar | | | comefrom foo | | | 2 | | | | | | 4 | | | | | +------------------------------------------------+------------------------------------------------+ When qualified, as in ``comefrom foo``, the program only jumps to that location from yield points in the block named ``foo``. Let's break down how this works. Execution begins at the line just after ``foo``, which prints ``1``. Cf0x10 has no magic names like "main;" if there is no code outside of a block, execution begins at the first block in the file. Next: #. Blank line yields #. Jumps to the ``bar`` block, at the line ``comefrom foo`` #. Prints ``2`` #. Blank line yields #. Jumps back to the ``foo`` block, at the line ``comefrom bar`` #. Prints ``3`` So far this is all pretty obvious, but now we get to the amazing part. How does ``4`` get printed? We just jumped back to ``foo``, so how do we get to ``bar`` again? Recall that the program jumped from the middle of the ``bar`` to ``foo``. After printing ``3``, the ``foo`` block ends and execution returns from whence it came. The next line in ``bar`` prints ``4``. Isn't structured programming great? Notice that even though the previous jump was from ``foo`` to ``bar``, execution never returned to the blank line in ``foo``, otherwise the program would have printed another ``3`` at the end. This is one way Cf0x10 blocks are significantly different, (and simpler!) than coroutines. Comefrom ordering ================= We previously saw that multiple eligible comefrom jumps are resolved in the order they appear in the source. This only applies, however, within the a single scope. When multiple comefroms are eligible targets in *separate* scopes, *all* of them will execute. Comefrom0x10 picks exactly one comefrom in each scope to execute, then jumps to the comefroms in a well-defined order. The comefrom in the current scope executes last, that is, after comefroms in nested scopes. This ordering is *not* recursive. That is, comefroms in nested scopes execute top-down, in source code order, depth-first. While these rules may seem complex, they are actually the most intuitive known solution to the multiple-comefrom problem [#]_. An example may make this more clear: +------------------------------------------------+------------------------------------------------+ | Program | Output | +================================================+================================================+ | :: | .. code-block:: none | | | | | outside | 1 | | # blocks cannot start with blank line | 2 | | | 3 | | "don't print this" | 4 | | comefrom | 5 | | 5 | | | inside | | | comefrom | | | "don't print this" | | | comefrom | | | 1 | | | deeper_one | | | comefrom | | | inception | | | comefrom | | | 3 | | | 2 | | | deeper_two | | | comefrom | | | 4 | | | | | +------------------------------------------------+------------------------------------------------+ When this program arrives at the first blank line in the ``outside`` block, *all* ``comefrom`` statements are eligible targets. They execute in this order: #. Second ``comefrom`` in the ``inside`` block, by the last-first and top-down rules #. Only ``comefrom`` in ``deeper_one``, by the first-first and top-down rules #. Only ``comefrom`` in the ``inception`` block, by the depth-first rule #. Only ``comefrom`` in ``deeper_two`` (figure out why) #. Only ``comefrom`` in the ``outside`` block, by the current-scope-last rule Notice that each block finishes executing before the program moves to the next comefrom. Scope ===== Naturally, you'll need to modularize your Cf0x10 programs. Block-scoping lets you do this without worrying about things like "classes" in other languages. Block scope works essentially the same way as in every other modern language, so we can put this all together into an idiomatic Cf0x10 program:: 'start program' until = 6 loops = 0 'n is ' n ' after ' loops count comefrom 'start counting' n = 0 # next comefrom if next next = 1/0 loops = loops + 1 n = n + 1 comefrom next if n < until 'n is ' n next = 1 'done counting' never_runs comefrom next if loops 'foobar' This is a rather complicated method of printing the numbers from 1 to 5. It prints: .. code-block:: none start program start counting n is 0 n is 1 n is 2 n is 3 n is 4 n is 5 done counting n is after 6 This is a big jump in complexity, so let's break it down line-by-line. The program starts at the first line, which just prints "start program":: 'start program' Since changing ``until`` does not affect any ``comefrom`` statements, there are no jumps at the assignment:: until = 6 Next, the blank line yields and the program jumps to the first line in the ``count`` block:: comefrom Then it prints "start counting":: 'start counting' Initializing ``n`` to zero actually does not cause a jump, because the only ``comefrom`` that refers to ``n`` is qualified by ``comefrom next``:: n = 0 The empty comment line is for aesthetic reasons, just to separate the block declaration for ``next`` from the lines above it:: # .. sidebar:: Exercise Why can't the empty comment be a blank line? Execution passes the ``next`` block declaration. Blank lines that immediately follow blocks do *not* trigger jumps, so the next line that executes is a comefrom. To help write robust programs, Cf0x10 is designed to avoid side-effects as much as possible, thus, executing a ``comefrom`` lines never does anything:: comefrom next if n < until Now, it prints the current value of ``n``, that is "n is 0":: 'n is ' n Whoa! It looks like the next line redefines ``next``. In fact, it does not. For easier programming, Cf0x10 doesn't have first-class blocks, as languages like Java has shown us that most programmers don't want or understand them. It's quite common in Cf0x10 to use a variable with the same name as a block. In this case, we're using it to cause a jump:: next = 1 Because ``next``, the variable changes from ``undefined`` to ``1``, the program continues in ``next``, the block:: comefrom if next Resetting ``next``, the variable, to ``undefined`` does not cause a jump, since the ``if next`` evaluates to falsy after this assignment (assigning to zero would have worked just as well):: next = 1/0 The variable ``loops`` is defined in the outermost scope. This does not trigger a jump into the block ``never_runs`` because the block ``next`` is not visible from the scope of ``never_runs``:: loops = loops + 1 Incrementing ``n`` triggers a jump back into the ``count`` block:: n = n + 1 So, ``count`` continues at:: comefrom next if n < until And it prints the updated value of ``n``, "n is 1":: 'n is ' n .. sidebar:: Buzzword! This is "cooperative multitasking" Resetting ``next``, the variable, jumps back into ``next``, the block, and repeats printing incremented values of ``n`` until ``6``, when ``if n < until`` evaluates to false. Instead of jumping, ``next``, the block, finishes execution. The last line executed before jumping into ``next`` was ``next = 1``, so the next line to execute prints "done counting":: 'done counting' Finally, the program returns to the line after the very first jump and prints "n is after 6", illustrating that ``n`` is defined only within the lexical scope of the ``count`` block, but ``loops`` is visible:: 'n is ' n ' after ' loops .. [#] In many languages with comefrom-based constructs, multiple comefroms are simply an error. See :doc:`../reference/comefrom` for more motivation.