On 2-Apr-09, at 4:25 PM, Per Eckerdal wrote:
But I still don't see any error in the main point of my previous mail, that is that only two different sets of globals are needed.
No... that's wrong. In the tower model each instance is an independent cell. If one of the levels is keeping state in one of these cells, you don't want it to interfere with the state in another cell (i.e. for the same variable at a different level). Keeping state is useful at the macro expansion level to keep tables of macros you have seen in the file being expanded.
State is not the only thing... you might store in a module's variable "v" some information which will not be the same at all levels (a timestamp, a data structure, a list, etc).
So you can't get out of this trouble by ordering the initializations.
Marc
Afficher les réponses par date
But I still don't see any error in the main point of my previous mail, that is that only two different sets of globals are needed.
No... that's wrong.
Haha I have seriously spent since I read your last mail (40mins) to try to understand that, but I finally think I do. Bummer.
It just doesn't feel good to have to resort to things with such great conceptual and performance overhead. How many levels are really used in practise, assuming that Gambit core can somehow be hacked away from this limitation? I don't think I've ever seen a macro that uses complex macro expansion in itself, let alone more levels. If interpretation with a cache with macro-expanded code for each used level is used mostly, wouldn't that work rather well? I can see libraries like srfi-1 needing to be instansiated at levels 1, 2 and maybe 3 but that's an extreme case. For these libraries it might be possible to add a declaration that states that multiple instances of this module is not required?
A more impure approach might be to require modules that require multiple instances to declare that explicitly. As I understand it most modules don't carry any state at all, and in these cases this isn't an issue. (With state I mean anything that isn't constant or isn't the same for every invocation of the module)
/Per
On 2-Apr-09, at 5:59 PM, Per Eckerdal wrote:
But I still don't see any error in the main point of my previous mail, that is that only two different sets of globals are needed.
No... that's wrong.
Haha I have seriously spent since I read your last mail (40mins) to try to understand that, but I finally think I do. Bummer.
It just doesn't feel good to have to resort to things with such great conceptual and performance overhead.
I too like simplicity. But is there an easy model (to implement) that is also elegant and powerful? The easier models I know are less elegant than the syntactic tower model.
How many levels are really used in practise, assuming that Gambit core can somehow be hacked away from this limitation? I don't think I've ever seen a macro that uses complex macro expansion in itself, let alone more levels. If interpretation with a cache with macro- expanded code for each used level is used mostly, wouldn't that work rather well? I can see libraries like srfi-1 needing to be instansiated at levels 1, 2 and maybe 3 but that's an extreme case. For these libraries it might be possible to add a declaration that states that multiple instances of this module is not required?
I appreciate your desire for a model that is easy to implement, but the kind of questions you are asking suggest to me that the end product will be a model that isn't pure. In other words it will work 90% of the time (hey maybe 99% of the time), but in those 1-10% of the time when it breaks down the user has no clue what he has done wrong. Let's not forget why we like Scheme! The elegance of the model must prevail over implementation complexity and performance (as long as it is not too complex or slow, as Einstein might put it).
A more impure approach might be to require modules that require multiple instances to declare that explicitly. As I understand it most modules don't carry any state at all, and in these cases this isn't an issue. (With state I mean anything that isn't constant or isn't the same for every invocation of the module)
It is not just a question of state. Each instance of a module must go through an initialization phase (i.e. the phase when the "define"s and the top-level expressions of the module are executed). What if a module contains this definition
(define start-time (current-time))
or
(define unique-id (random-integer 1000000000000000))
or
(define t (thread-start! (make-thread (lambda () ...))))
or
(define v (list 1 2 3))
In all of these cases it may be important to run the initialization phase for each instance of the module, to get a correct start-time of the instance, a unique identifier for that instance, a thread per instance, or unique objects for each instance (to distinguish them with eq?).
Marc
How many levels are really used in practise, assuming that Gambit core can somehow be hacked away from this limitation? I don't think I've ever seen a macro that uses complex macro expansion in itself, let alone more levels. If interpretation with a cache with macro- expanded code for each used level is used mostly, wouldn't that work rather well? I can see libraries like srfi-1 needing to be instansiated at levels 1, 2 and maybe 3 but that's an extreme case. For these libraries it might be possible to add a declaration that states that multiple instances of this module is not required?
I appreciate your desire for a model that is easy to implement, but the kind of questions you are asking suggest to me that the end product will be a model that isn't pure. In other words it will work 90% of the time (hey maybe 99% of the time), but in those 1-10% of the time when it breaks down the user has no clue what he has done wrong. Let's not forget why we like Scheme! The elegance of the model must prevail over implementation complexity and performance
That's just not true. What I proposed above is 100% pure with the addition of a declaration that makes it possible to say when not to lose lots of performance when it's not needed. It is not incorrect to have separate compilations of a module for each level. And, in almost all cases, it's even correct not to have it.
But, as you say, doing most cases right isn't really good enough. But still, one of my main design goals with Black Hole is that it should not add any run-time overhead. And hey, with Gambit as it is now, it's virtually impossible to express what we're talking about.
One of the really cool things with Gambit is that it is fast. Lose that, and a quite strong argument for using it is lost.
(as long as it is not too complex or slow, as Einstein might put it).
So, in this case, to me it seems like it actually is too complex and too slow. It is better to require the user to know what he's doing when writing ridiculously complex macros that are practically impossible to express in the current system anyway than to lose significant performance everywhere.
And yes, if used correctly, forcing the user to do a (declare (state))- style thing, and letting that mean forcing one compilation per instance level, is correct, albeit maybe not very pure. And c'mon, if you're writing macros that rely on state, you probably know it, so when you encounter a problem, just add the declaration and see if it goes away, it's not that difficult.
A more impure approach might be to require modules that require multiple instances to declare that explicitly. As I understand it most modules don't carry any state at all, and in these cases this isn't an issue. (With state I mean anything that isn't constant or isn't the same for every invocation of the module)
It is not just a question of state. Each instance of a module must go through an initialization phase (i.e. the phase when the "define"s and the top-level expressions of the module are executed). What if a module contains this definition
(define start-time (current-time))
or
(define unique-id (random-integer 1000000000000000))
or
(define t (thread-start! (make-thread (lambda () ...))))
or
(define v (list 1 2 3))
In all of these cases it may be important to run the initialization phase for each instance of the module, to get a correct start-time of the instance, a unique identifier for that instance, a thread per instance, or unique objects for each instance (to distinguish them with eq?).
I tried to clarify in the parenthesis that with state I also mean this. I do think that it's reasonable to call a thread that a module has, state.
/Per
It is not just a question of state. Each instance of a module must go through an initialization phase (i.e. the phase when the "define"s and the top-level expressions of the module are executed). What if a module contains this definition
(define start-time (current-time))
or
(define unique-id (random-integer 1000000000000000))
or
(define t (thread-start! (make-thread (lambda () ...))))
or
(define v (list 1 2 3))
In all of these cases it may be important to run the initialization phase for each instance of the module, to get a correct start-time of the instance, a unique identifier for that instance, a thread per instance, or unique objects for each instance (to distinguish them with eq?).
Globals also give rise to another kind of problem in module systems. What should happen when re-importing a module? Should the globals be wiped? Depending on what they contain, often that is not desirable. What I have done so far is simply to not use them. This is of course less powerful than having them, but it also yields nicer programs most of the time. I still don't know how I think globals are best implemented, from this perspective. Here I don't see a pure and difficult vs. simple but incorrect situation. /Per
gambit-modules-list@iro.umontreal.ca