11

IMHO binding a variable to another variable or an expression is a very common scenario in mathematics. In fact, in the beginning, many students think the assignment operator(=) is some kind of binding. But in most of the languages, binding is not supported as a native feature. In some languages like C#, binding is supported in some cases with some conditions fulfilled.

But IMHO implementing this as a native feature was as simple as changing the following code-

int a,b,sum;
sum := a + b;
a = 10;
b = 20;
a++;

to this-

int a,b,sum;
a = 10;
sum = a + b;
b = 20;
sum = a + b;
a++;
sum = a + b;

Meaning placing the binding instruction as assignments after every instruction changing values of any of the variable contained in the expression at right side. After this, trimming redundant instructions (or optimization in assembly after compilation) will do.

So, why it is not supported natively in most of the languages. Specially in the C-family of languages?

Update:

From different opinions, I think I should define this proposed "binding" more precisely-

  • This is one way binding. Only sum is bound to a+b, not the vice versa.
  • The scope of the binding is local.
  • Once the binding is established, it cannot be changed. Meaning, once sum is bound to a+b, sum will always be a+b.

Hope the idea is clearer now.

Update 2:

I just wanted this P# feature. Hope it will be there in future.

Gulshan
  • 9,402
  • 10
  • 58
  • 89
  • 14
    Possibly because any compiler developer who has tried to add this feature to C has been hunted down and shot. – Pete Wilson Apr 09 '11 at 16:01
  • I am talking about one-way (right to left) binding, not two-way. The binding will always affect only one variable. – Gulshan Apr 09 '11 at 16:52
  • Totally understood Gulshan, but Pete's comment was still hilarious. – Mike Rosenblum Apr 09 '11 at 16:53
  • 2
    For what it's worth, this kind of programmatic point of view is on the rise: reactive programming. What you are describing is also embodied by spreadsheet programs such as Excel, which are essentially data binding (or reactive programming) on steroids. – Mike Rosenblum Apr 09 '11 at 16:55
  • 11
    It might not be a feature of "most" programming languages, but it *is* a feature of the *most popular* programming language: Excel. – Jörg W Mittag Apr 09 '11 at 18:04
  • 2
    Thanks to expression trees, this is already possible in C#. I blogged about it yesterday: http://happynomad121.blogspot.com/2013/01/data-binding-among-complex-expressions.html – HappyNomad Feb 02 '13 at 04:31

6 Answers6

9

You're confusing programming with math. Not even functional programming is entirely math, even though it borrows many ideas and turns them into something that can be executed and used for programming. Imperative programming (which includes most C-inspired languages, notable exceptions being JavaScript and the more recent additons to C#) has nearly nothing to do with math, so why should these variables behave like variables in math?

You have to consider that this is not always what you want. So many people are bitten by closures created in loops specifically because closures keep the variable, not a copy of its value at some point, i.e. for (i = 0; i < 10; i++) { var f = function() { return i; }; /* store f */ } creates ten closures which return 9. So you'd need to support both ways - which means twice the cost on the "complexity budget" and yet another operator. Possibly also incompabilities between code using this and code not using this, unless the type system is sophisticated enough (more complexity!).

Also, implementing this efficently is very hard. The naive implementation adds a constant overhead to every assignment, which can add up quickly in imperative programs. Other implementations might delay updates until the variable is read, but that's significantly more complex and still has overhead even when the variable is never read again. A sufficently smart compiler can optimize both, but sufficently smart compilers are rare and take much effort to create (note that it's not always as simple as in your example, especially when the variables have broad scope and multithreading comes into play!).

Note that reactive programming is basically about this (as far as I can tell), so it does exist. It's just not that common in traditional programming languages. And I bet some of the implementations problems I listed in the previous paragraph are solved.

  • I think you have 3 points- 1) It is not imperative style programming. These days most of the languages are not confined with some paradigms. I don't think this is out of this paradigm style is a good logic. 2) Complexity. You have shown many more complex things are already supported. Why not this one? I know operators having almost no use are being supported. And this is a totally new feature. So how there can be the compatibility issue. I am not changing or wiping out something. 3) Hard Implementation. I think the present day compilers has already have the ability to optimize this. – Gulshan Apr 09 '11 at 16:47
  • 1
    @Gulshan: (1) *No* programming paradigm is math. FP is close, but FP is relatively rare and the impure FP languages with mutable variables don't treat these variables as if they were from math either. The one paradigm where this exists is reactive programming, but reactive programming is not known or used widely (in traditional programming languages). (2) Everything has some complexity cost and a value. This has rather high cost and IMHO relatively little value except in a few domains, so it's not the first thing I'd add to *my* language unless it specifically targeted these domains. –  Apr 09 '11 at 16:54
  • Why not have one more tool in our box? Once a tool is there, people will make use of it. A question. If some language like C or C++ want to implement this feature and ask for your opinion, would you say, "Don't give it. Because it will be too hard for you and people will mess up with this."? – Gulshan Apr 09 '11 at 17:00
  • @Gulshan: Regarding (3): It's not always (not to say, rarely) as easy as in your example. Put it into global scope and you suddenly need link-time optimizations. Then add dynamic linking, and you can't even do *anything* at compiletime. You need a very smart compiler *and* a very clever runtime including JIT to do it well. Also consider the amount of assignments in every non-trivial program. Neither you nor I can write these components. There are at most a few people who can and are interested in this subject. And they might have better things to do. –  Apr 09 '11 at 17:01
  • @Gulshan: I'd ask them not to add feature to the incredbly complex beast C++ already is, or I'd ask them not to try using C for stuff beyond system programming (which is not one of the domains where this is very useful). Apart from that, I'm all for exciting new features, but only when there's enough of the complexity budget left (many languages always exhausted theirs) and the feature is useful for what the language is intended to do - as I said before, there are only a few domains where this is useful. –  Apr 09 '11 at 17:04
  • Then just make scope of the binding local and exclude dynamic linking support. Even C++ templates have issues with dynamic linking. – Gulshan Apr 09 '11 at 17:05
  • @Gulshan: But then "I am not changing or wiping out something." doesn't hold any more. That's also a **huge** price to pay - it severely limits the usefulness of this feature and prevents an incredibly useful feature many many people use. Also, those were just two examples of problems off the topof my head. One can propably find many more. –  Apr 09 '11 at 17:11
  • @delnan - I think your closure example is broken. Don't know from what language this is supposed to be, but it's broken. – Ingo Apr 09 '11 at 17:12
  • @Ingo: It works that way at least in Python, Javascript and C#. I'm pretty sure it works the same way in most languages. –  Apr 09 '11 at 17:13
  • @delnan - I am sorry to hear that this old lisp bug^W feature has made it into modern languages. In Haskell, of course [ const i | i <- [0..9] ] would return a list of ten different functions. – Ingo Apr 09 '11 at 17:16
  • @Ingo: Of course it does. The other languages return different functions as well, but all refer to the same, mutable variable (instead of copying its value at closure creation time), which of course changes during the loop. Pure FP languages don't have that issue as their variables are immutable. –  Apr 09 '11 at 17:18
  • To be precise, there are no variables at all, just bindings (to come back on topic:). But now I understand better why people accuse Java of not having "real" closures. Perhaps they want this behaviour. – Ingo Apr 09 '11 at 19:51
  • I find it puzzling that given the horrors from QBasic-derivatives' default pass-by-reference semantics, the designers of C# had it *silently* impart pass-by-reference semantics to variables in closures. I appreciate that there are times when it's useful to close over things by reference, but in all such cases I can think of, the burden of having to explicitly specify one's intention would be minor compared with that of having to guess what live references to a variable might be floating around the universe. – supercat Mar 09 '15 at 21:01
3

I think what you're describing is called a Spreadsheet:

A1=5
B1=A1+1
A1=6

...then evaluating B1 returns 7.

EDIT

The C language is sometimes called "portable assembly". It's an imperative language, whereas spreadsheets, etc., are declarative languages. Saying B1=A1+1 and expecting B1 to re-evaluate when you change A1 is definitely declarative. Declarative languages (of which functional languages are a subset) are generally considered higher level languages, because they're farther away from how the hardware works.

On a related note, automation languages like ladder logic are typically declarative. If you write a rung of logic that says output A = input B OR input C it's going to re-evaluate that statement constantly, and A can change whenever B or C changes. Other automation languages like Function Block Diagram (which you might be familiar with if you've used Simulink) are also declarative, and execute continually.

Some (embedded) automation equipment is programmed in C, and if it's a real-time system, it probably has an infinite loop that re-executes the logic over and over, similar to how ladder logic executes. In that case, inside your main loop you could write:

A = B || C;

...and since it's executing all the time, it becomes declarative. A will constantly be re-evaluated.

Scott Whitlock
  • 21,874
  • 5
  • 60
  • 88
3

C, C++, Objective-C:

Blocks provide the binding feature that you're looking for.

In your example:

sum := a + b;

you're setting sum to the expression a + b in a context where a and b are existing variables. You can do exactly that with a "block" (a.k.a. closure, a.k.a. lambda expression) in C, C++, or Objective-C with Apple's extensions (pdf):

__block int a = 0, b = 0;           // declare a and b
int (^sum)(void);                   // declare sum
sum = ^(void){return a + b;};       // sum := a + b

This sets sum to a block that returns the sum of a and b. The __block storage class specifier indicates that a and b may change. Given the above, we can run the following code:

printf("a=%d\t b=%d\t sum=%d\n", a, b, sum());
a = 10;
printf("a=%d\t b=%d\t sum=%d\n", a, b, sum());
b = 32;
printf("a=%d\t b=%d\t sum=%d\n", a, b, sum());
a++;
printf("a=%d\t b=%d\t sum=%d\n", a, b, sum());

and get the output:

a=0      b=0     sum=0
a=10     b=0     sum=10
a=10     b=32    sum=42
a=11     b=32    sum=43

The only difference between using a block and the "binding" that you propose is the empty pair of parentheses in sum(). The difference between sum and sum() is the difference between an expression and the result of that expression. Note that, as with functions, the parentheses don't have to be empty -- blocks can take parameters just as functions do.

Caleb
  • 38,959
  • 8
  • 94
  • 152
3

It fits very poorly with most models of programming. It would represent a kind of completely uncontrolled action-at-a-distance, in which one could destroy the value of hundreds or thousands of variables and object fields by making a single assignment.

jprete
  • 1,509
  • 11
  • 16
  • Then I would suggest to make a rule that, the bound variable ie the left hand side will not be changed by any other way. And what I am talking about is just one way binding, not two-way. If you follow my question, you can see. So, no other variable will be affected. – Gulshan Apr 09 '11 at 16:50
  • That doesn't matter. Every time you write to `a` or `b`, you need to consider its impact on every single place that `sum` is used, and every place that you read `sum` you need to consider what `a` and `b` are doing. For non-trivial cases, that could get complicated, especially if the actual expression bound to `sum` can change at runtime. – jprete Apr 09 '11 at 17:43
  • I would recommend the rule that the binding expression cannot be changed once the binding is done. Even assignment will be impossible. It will be like a semi-constant once bound to an expression. That means, once sum is bound to a+b, it will always be a+b, during the rest of the program. – Gulshan Apr 09 '11 at 23:55
3

Ya' know, I have this nagging gut feeling that reactive programming might be cool in a Web2.0 environment. Why the feeling? Well, I have this one page that's mostly a table that changes all the time in response to table-cell onClick events. And cell clicks often mean changing the class of all cells in a col or row; and that means endless loops of getRefToDiv( ), and the like, to find other related cells.

IOW, many of the ~3000 lines of JavaScript I've written do nothing but locate objects. Maybe reactive programming could do all that at small cost; and at a huge reduction in lines of code.

What do you guys think about that? Yes, I do notice that my table has a lot of spreadsheet-like features.

Pete Wilson
  • 1,766
  • 11
  • 15
2

C++

Updated to be generic. Parameterized on return and input types. Can supply any binary operation satisfying the parameterized types. Code computes the result on demand. It tries to not recompute results if it can get away with it. Take this out if this is undesireable (because of side-effects, because the contained objects are large, because of whatever.)

#include <iostream>

template <class R, class A, class B>
class Binding {
public:
    typedef R (*BinOp)(A, B);
    Binding (A &x, B &y, BinOp op)
        : op(op)
        , rx(x)
        , ry(y)
        , useCache(false)
    {}
    R value () const {
        if (useCache && x == rx && y == ry) {
            return cache;
        }
        x = rx;
        y = ry;
        cache = op(x, y);
        useCache = true;
        return cache;
    }
    operator R () const {
        return value();
    }
private:
    BinOp op;
    A &rx;
    B &ry;
    mutable A x;
    mutable B y;
    mutable R cache;
    mutable bool useCache;
};

int add (int x, int y) {
    return x + y;
}

int main () {
    int x = 1;
    int y = 2;
    Binding<int, int, int> z(x, y, add);
    x += 55;
    y *= x;
    std::cout << (int)z;
    return 0;
}
Thomas Eding
  • 1,062
  • 1
  • 11
  • 18