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
8
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.
0
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.
1
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
0