1

In the pseudo code below, the variable key must be declared and initialized outside of the context which it is used/relevant because there are two disparate if-blocks with the exact same condition.

I was thinking it might be cool if a language could consider two if blocks like that to have the same scope. What are the drawbacks, if any, to this approach?

Before:

class Foo
  static bool CacheEnabled = true
  static Foo Get(string name)
    Foo result
    string key = "cache_" + name
    if(CacheEnabled)
      if(Cache.GetFoo(key, out result))
        return result
    result = new Foo()
    //
    // rest of initialization here..
    //
    if(CacheEnabled)
      Cache.PutFoo(key, result)
    return result

After

class Foo
  static bool CacheEnabled = true
  static Foo Get(string name)
    Foo result
    if(CacheEnabled)
      string key = "cache_" + name
      if(Cache.GetFoo(key, out result))
        return result
    result = new Foo()
    //
    // rest of initialization here..
    //
    if(CacheEnabled)
      Cache.PutFoo(key, result) // key is available since the condition is the same
    return result
Karl Bielefeldt
  • 147,435
Aaron Anodide
  • 5,553
  • 6
  • 30
  • 37
  • 6
    Don't make your language crazier than PHP please. – Thomas Eding Oct 10 '14 at 21:04
  • 1
    Just a reminder. A downvote isn't for "this is a bad idea," it's for "this is a bad question." It's a good question, with objective answers. – Karl Bielefeldt Oct 10 '14 at 21:28
  • 4
    It's not even clear to me how to formalize this properly, in particular the "same condition" bit. You can't just check if the condition is syntactically the same, since it might evaluate differently the second time (consider anything non-deterministic, or anything dependent on mutable state altered in rest of initialization). Even if you restrict it to bare variables as in your example, if they're not local (the one in your example isn't) it's often -- sometimes literally impossible -- to computationally check that it isn't changed between the two conditions. –  Oct 10 '14 at 21:37
  • I accidentally a word in the comment above: "it's often infeasible -- sometimes literally impossible". tl;dr PLT is hard. –  Oct 10 '14 at 21:52
  • @delnan, you should expand that into an answer. – Karl Bielefeldt Oct 10 '14 at 21:53
  • @KarlBielefeldt I might, after seeing OP's reaction to judge the level at which the question resides, and whether OP can come up with any additional details. –  Oct 10 '14 at 21:59
  • 2
    @delnan: To me it looks like "same condition" is essentially the Function Equivalence Problem, which is of course known to be undecidable for Turing-complete languages. I don't know many languages in which Function Equivalence is decidable, but those that I do know are extremely restricted: no variables, only a small fixed set of pure, total functions, no I/O, no side-effects, no external libraries, no user-defined types, no user-defined functions. Essentially, a dumb desktop calculator. – Jörg W Mittag Oct 10 '14 at 22:46
  • The "problem" the OP describes here could be resolved much more simply if variable scope was shared across an entire method definition. – Alex D Nov 06 '14 at 11:04

3 Answers3

6

Lexical scope is a compile-time (actually, even a parse time) property. "Same condition" is a runtime property. Figuring out runtime properties at compile time is hard. In fact, it is almost always impossible. Even the simplest, stupidest possible runtime property, namely, "will this run forever" (aka the Halting Problem) is impossible to figure out at compile-time.

As it turns out, your "same condition" is basically the Function Equivalence Problem, which, just like the Halting Problem is known to be undecidable. IOW: it is impossible for the compiler to figure out that the two blocks have the same condition.

Note, however, that all of this only applies for Turing-complete languages. If you restrict what your programmers are allowed to put into a condition enough, then you might be able to prove at compile time that the two conditions are the same. Note also, that it takes very little to make a language Turing-complete, or put another way: you have to take an awful lot of features away from a language to make Function Equivalence decidable.

Whether or not it is worth it to basically disallow powerful if statements just to save some variable initialization is a matter of taste, I guess.

Jörg W Mittag
  • 103,514
3

I would vote no, because its availability would tend to encourage duplication and mixing abstraction levels. For your example, you are mixing the caching concern with the calculation concern, and duplicating the conditional. Both of these problems could be mitigated by moving the conditional and the key to the Cache class. There are other language constructs, like first-class functions and decorators, that can handle this kind of use case much more cleanly.

There may be other use cases that would fit better, but I can't think of any that wouldn't involve duplicating of the conditional, and therefore couldn't be handled more cleanly with some refactoring.

Karl Bielefeldt
  • 147,435
  • good points. but still, it makes inlining cross cutting concerns a bit cleaner, if that's what you've decided to do. effectively, it's like putting an overlay on the code, bounded by that condition. i can imagine an editor's collapsing function to make them both open and close at the same time. – Aaron Anodide Oct 10 '14 at 22:35
2

As Jörg W Mittag has said, trying to get the compiler to prove that two conditions are equivalent is undecidable. The user would have to annotate which conditions are equivalent, and what happens if they annotate incorrectly?

This would also make scopes a lot more complicated to reason about (and certainly even more daunting for new programmers). Mysterious variables popping in and out of scope can also be another source of bugs.

Furthermore, what you want could be done entirely using first-class functions:

class Foo
  static bool CacheEnabled = true

  static Foo Get(string name)
    Foo initializer() = () =>
      // the variable "key" is inaccessible here
      Foo result = new Foo()
      //
      // rest of initialization here..
      //
      return result

    Foo result
    string key = "cache_" + name
    if(CacheEnabled)
      if(Cache.GetFoo(key, out result))
        return result

    // call initializer
    result = initializer()

    if(CacheEnabled)
      Cache.PutFoo(key, result)
    return result
Rufflewind
  • 2,237