In OOP, static scope means the scope is linked to the class, and instance scope is linked to a specific instance of the class.
Some languages support static locals , which allow a value to be retained from one call of a function to another, however still in static scope.
As you might conclude from some other posts by me here, you can tell I'm a big fan of encapsulation.
Sometimes I use a certain instance variable within a class, only within one method. E.g. a flag indicating whether a certain method has already been called. My convention (following good standards) is to place these variables right before the function, close to their usage. Ideally, I would be able to place them within the function scope. Following the naming scheme of static locals, let's call this hypothetical scope function locals.
Darien reworded this very clearly in a comment:
Ah, so what you really want is something beyond even private protection, which is so closely protected that it is only visible to a single method within a single instance.
Now I know you could say, following the Single Responsibility Principle, this method and the associated variables should most likely be split into a separate class. To verify this - and I challenge you to do the same - I looked into some random code files and quickly found source files (even < 100 lines) where such a 'function local' could be used.
Actually, C# offers a solution to this for one common scenario out of the box. Auto-implemented properties hide the backing field.
This idea might be far-fetched, but somehow I feel it would make sense. Does anybody know of any language which supports this?
4 Answers 4
In Python it's easy:
class TheClass(object):
def __init__(self):
def closure(times_called):
def new_func():
self.func = closure(times_called + 1)
""" other stuff goes here """
return times_called
return new_func
self.func = closure(1)
o = TheClass()
print o.func()
print o.func()
print o.func()
gives:
1
2
3
Normally, methods in Python are functions that are stored (by reference) in the class dict. If a "static" variable would be bound to such a function, then all instances would share the same variable. The solution is to give every instance its own version of the method in the form of an instance attribute, which is stored (by reference) in the instance dict, like any other instance variable. The creation of this method (new_func()
) is wrapped in a closure that binds the "function-static" variable (times_called
). Every call of the method also creates a new version of the method. It's a very Lispy (Lispish? Lispsome?) thing, though. In Lisp, state and state transitions are often wrapped in a similar way.
Also, the concept of a static scope is not inherent to OO. There's no static scope in Python, for example. Just package, module, function and class scope, and classes are also objects. The idea of a static scope, to me, is quite un-OO. OO is all about state and polymorphism. Polymorphic messages can be implemented without classes but instead with Multi-Methods, Prototypes, Type Constructors a la Haskell, ..., and state and state transitions with closures. Lexical scoping is nice, though. Python doesn't have it, but instead the global
and nonlocal
keywords, which are just crutches instead of the real thing.
Anyway, I'd not use the solution above, but instead put the state in an instance attribute, where it belongs in Python. Im not very concerned with strong encapsulation. If I were, I wouldn't be using Python, where there's no concept of privacy at all. It's a Dutch language! They have nude beaches!
This idea might be far-fetched, but somehow I feel it would make sense. Does anybody know of any language which supports this?
The wiki link you posted mentions Perl and Ruby, but I'd add PHP:
function test()
{
static $a = 0;
echo $a; // Prints a different number each time test() is called
$a++;
}
Edit: A possible "fake instance-method-local" solution, albeit a memory-use risk:
class Foo(){
function test(){
static $a = array();
if(!array_key_exists($this,$a)){
$a[$this] = 0;
}
echo $a[$this]; // Prints a different number each time test() is called per instance
$a[$this] += 1;
}
}
Edit2: Looking back at that last block... It adds more ways to make a typo and introduce bugs than it helps you with encapsulation. Unless you have good syntactic sugar, the capability simply isn't worth it.
-
@Darien: isn't that a static local? Meaning if you place it in a class, every call to
test()
(regardless of the instance) would use the same$a
?Steven Jeuris– Steven Jeuris2011年06月07日 00:35:07 +00:00Commented Jun 7, 2011 at 0:35 -
Maybe I misunderstood, isn't that what you were looking for, except in a language that required no declarations or other references outside the method-body?Darien– Darien2011年06月07日 00:38:30 +00:00Commented Jun 7, 2011 at 0:38
-
@Darien: I updated the question: "Sometimes I use a certain instance variable within a class, only within one method."Steven Jeuris– Steven Jeuris2011年06月07日 00:40:06 +00:00Commented Jun 7, 2011 at 0:40
-
2Ah, so what you really want is something beyond even
private
protection, which is so closely protected that it is only visible to a single method within a single instance.Darien– Darien2011年06月07日 00:42:07 +00:00Commented Jun 7, 2011 at 0:42 -
@Darien: Very well put! I added it to the question.Steven Jeuris– Steven Jeuris2011年06月07日 00:44:54 +00:00Commented Jun 7, 2011 at 0:44
I've wanted the same thing in the past. It's easiest in a dynamic language, where it's possible to associate the data with the instance at runtime.
In C++, which is statically typed, the compiler must be able to deduce the size of a class given only its declaration; with instance-local variables declared inside method definitions, the compiler can no longer do this. C# could potentially handle it (as could Java) because the declaration and definition are not decoupled as they are in C++.
A hypothetical C++-esque solution would associate instance variables with particular methods:
class Example {
public:
void method_the_first();
void method_the_other();
// Externally accessible as "this->method_the_first::spam".
int method_the_first()::spam;
private:
// Not externally accessible.
int method_the_other()::eggs;
};
You could also assume that all method-static data is private:
class Example {
public:
void { int spam; } method_the_first();
void { int bar; } method_the_other();
};
-
I actually just created a working, albeit hacky, solution in C#.Steven Jeuris– Steven Jeuris2011年06月07日 01:40:47 +00:00Commented Jun 7, 2011 at 1:40
It can be done in C# as well, and possibly other languages which support lambdas! Although the following is still pretty experimental, it works.
Test instance1 = new Test();
Test instance2 = new Test();
instance1.SomeFunction(); // functionLocal = 1, staticLocal = 1
instance1.SomeFunction(); // functionLocal = 2, staticLocal = 2
instance2.SomeFunction(); // functionLocal = 1, staticLocal = 3
instance2.SomeFunction(); // functionLocal = 2, staticLocal = 4
class Test
{
public void SomeFunction()
{
// 'function local' as discussed in the question
Local<int> functionLocal = Local<int>.Instance( () => this );
// static local, normally not possible in c#
Local<int> staticLocal = Local<int>.Static( () => {} );
functionLocal.Value += 1;
staticLocal.Value += 1;
}
}
The lambda is only created once, allowing to link the place where it was created to its scope. The variables are stored in the Local<T>
class as Local<T>
instances, in a static context.
Static locals aren't supported in C#, but by linking the instance to the passed lambda, the correct scope can be found.
The 'function locals' are possible with a bit more effort by using a lambda which returns the current 'instance' scope as well.
An implementation of Local<T>
can be found on this relevant SE question.
Explore related questions
See similar questions with these tags.
personal
scope wouldn't be useful (read: convenient), but I feel that most of those cases could be solved in another manner that more closely relates to what you want to do. In the case of a flag for every method, you could create an associative array of flags and checkflags[fnName]
because you wont have collisions on the names of functions.