Comment by MillenialMan

5 years ago

The problem being solved here is just scope, not re-usability. Functions are a bad solution because they force non-locality. A better way to solve this would be local scope blocks, /that define their dependencies.

E.g. something like:

    (reads: var_1, var_2; mutates: var_3) {
       var_3 = var_1 + var_2
    }

You could also define which variables defined in the block get elevated, like return values:

    (reads: var_1, var_2; mutates: var_3) {
       var_3 = var_1 + var_2
       int result_value = var_1 * var_2
    } (exports: result_value)

    return result_value * 5

This is also a more tailored solution to the problem than a function, it allows finer-grained control over scope restriction.

It's frustrating that most existing languages don't have this kind of feature. Regular scope blocks suck because they don't allow you to define the specific ways in which they are permeable, so they only restrict scope in one direction (things inside the scope block are restricted) - but the outer scope is what you really want to restrict.

You could also introduce this functionality to IDEs, without modifying existing languages. Highlight a few lines, and it could show you a pop-up explaining which variables that section reads, mutates and defines. I think that would make reading long pieces of code significantly easier.

This is one of the few comments in this entire thread that I think is interesting and born out of a lot of experience and not cargo culting.

In C++ you can make a macro function that takes any number of arguments but does nothing. I end up using that to label a scope because that scope block will then collapse in the IDE. I usually declare any variables that are going to be 'output' by that scope block just above it.

This creates the ability to break down isolated parts of a long function that don't need to be repeated. Variables being used also don't need to be declared as function inputs which also simplifies things significantly compared to a function.

This doesn't address making the compiler enforce much, though it does show that anything declared in the scope doesn't pollute the large function it is in.

  • Thank you. Your macro idea is interesting, but I definitely want to be able to defer to the compiler on things like this. I want my scope restrictions to also be a form of embedded test. Similar to typing.

    I wish more IDEs had the ability to chunk code like this on-the-fly. I think it's technically possible, and maybe even possible to insert artificial blocks automatically, showing you how your code layout chunks automatically... Hmm.

    You know, once I'm less busy I might try implementing something like this.

C++ lambda captures work exactly like this. You need to state which variables that should be part of the closure and whether they should be mutable and by reference or copies.

    auto result_value = [var1, var2, &var3]() {
        var3 = var1 + var2
        return var1 * var2
    }()
    return result_value * 5

Does anyone know if compiler is smart enough to inline self-executing lambda as above? Or will this be less performant than plain blocks?

Ada/SPARK actually has dependencies like that as part of function specs. Including which variables depend on what.