Mailing List Archive: 49091 messages
  • Home
  • Script library
  • AltME Archive
  • Mailing list
  • Articles Index
  • Site search
 

local vars in functions

 [1/40] from: rishioswal:yah:oo at: 23-Apr-2002 20:08


One part of REBOL that can feel odd is when i have to define local variables of a function in the first block of the function. I find it a bit annoying to have to add new variables to the list for every little local variable I use. In addition, I feel it clutters up the first block and increases function size. In addition, the way it is currently done could make it easy to create hard to detect bugs. What I would like to see is another shortcut to creating local variables in any context (function, innerfunction, loop). The obvious way I see of doing this is as follows: myfunc: func [][ localvar:: $25 myinnerfunc: func [][ innerlocal:: $10 print localvar ; prints $25 ] print innerlocal; error! ] print localvar ; error! using the "::" for local var will make it more convienient to create local vars (which i use all the time over global vars). In addition, it will help prevent some errors of accidental global var creation because it is now easy to spot a local var. Best of all, this type of shortcut would not break anything in rebol. You could even use this in a loop: for count 1 10 1 [ localvar:: "hello" ] Using the "::" shortcut in a global context would be the same as using a ":". The disadvantage I see is that it adds another thing to the language.. But consider that now we could stop using the /local keyword, reduce bugs, and use it consistently everywhere, overall it can simplify things. Anybody have other reasons as to why it was not done this way?? Perhaps there is a performance issue?? rishi

 [2/40] from: tim:johnsons-web at: 23-Apr-2002 19:55


* Rishi Oswal <[rishioswal--yahoo--com]> [020423 19:18]:
> One part of REBOL that can feel odd is when i have to > define local variables of a function in the first
<<quoted lines omitted: 4>>
> addition, the way it is currently done could make it > easy to create hard to detect bugs.
Yeah. I've been wondering why rebol does that.... Could it be better to make any 'word local by default, and make that word global optionally with a key word and maybe another keyword to 'protect it? I'm sure there is a good reason for the current approach, but I don't know what it is. -tim-
> What I would like to see is another shortcut to > creating local variables in any context (function,
<<quoted lines omitted: 38>>
> [rebol-request--rebol--com] with "unsubscribe" in the > subject, without the quotes.
-- Tim Johnson <[tim--johnsons-web--com]> http://www.alaska-internet-solutions.com http://www.johnsons-web.com

 [3/40] from: chrismorency:videotron:ca at: 24-Apr-2002 0:26


Hi, Have you looked at function ? ie myfunc: function [arg1 arg2] [local1 local2] [source] or myfunc: function [ arg1 arg2 ] [ local1 local2 ] [ source ] it pretty much what you want, plus if you use the following

 [4/40] from: chalz:earthlink at: 24-Apr-2002 0:49


> One part of REBOL that can feel odd is when i have to > define local variables of a function in the first
<<quoted lines omitted: 4>>
> addition, the way it is currently done could make it > easy to create hard to detect bugs.
Not that you /have/ to. This is only to hide variables from the rest of the code. If you don't care about them being freely available, then leave the /local section empty.

 [5/40] from: rishioswal:yaho:o at: 23-Apr-2002 21:19


I think the reason for making words global by default is simply because it is the natural thing for a newbie to the language or beginner to use. I think this was the right decision.. fits with the rebol philosophy.. The "::" solution, i feel, has the benefit of not getting in the way of beginners but making things more efficient/less errorprone for the intermediate/advanced.. well - at least it seems that way to me at first glance... rishi
>>>
Yeah. I've been wondering why rebol does that.... Could it be better to make any 'word local by default, and make that word global optionally with a key word and maybe another keyword to 'protect it? I'm sure there is a good reason for the current approach, but I don't know what it is. -tim- <<< --- Rishi Oswal <[rishioswal--yahoo--com]> wrote:

 [6/40] from: anton:lexicon at: 24-Apr-2002 17:29


Interesting idea Rishi, but I don't really like it. I am sure I would go mad trying to distinguish single or double colons everywhere. I think it would replace one type of hard-to-detect bug with another. (ie. accidentally writing only one colon.) The way rebol is at the moment is explicit and quite clear. Regards, Anton.

 [7/40] from: carl:cybercraft at: 24-Apr-2002 23:20


On 24-Apr-02, Rishi Oswal wrote:
> Using the "::" shortcut in a global context would be > the same as using a ":".
<<quoted lines omitted: 5>>
> Anybody have other reasons as to why it was not done > this way??
I like the idea and the syntax, but what happens with the likes of this... a: 10 a:: 20 a: 30 Does that produce an 'a in both the global and local context? And if so, does the third expression above change the local or global value, or both? Afterall, we can do this in REBOL...
>> a: 10 b: 20
== 20
>> fun: has [a][a: 50 print a print b] >> fun print a
50 20 10 Though I doubt it's good practice. (: -- Carl Read

 [8/40] from: brett::codeconscious::com at: 24-Apr-2002 21:36

REBOL just a programming language? (was local vars in functions)


Hi,
> One part of REBOL that can feel odd is when i have to > define local variables of a function in the first > block of the function. I find it a bit annoying to > have to add new variables to the list for every little > local variable I use.
No doubt you are referring to the sequence: func [...] [...] Which bit is the actual function? (a) the first bit (b) the second bit (c) the third bit (d) all of the above (e) none of the above My preference is for answer (e) however I recognise that it is convenient to talk as if it were (d). But your suggestion of the double colon to indicate "local" variables gives the feeling it might be (c). What if I changed this to: func [...] relative-expression where relative-expression is a word set to a value of type block! So now, if relative-expression (the block) defined what was local and what was global would it still be relative? Relative to what?! - I think that question is the answer to the mystery/purpose of REBOL. :^) My obscure position is that the first block defines what in the second block will *become* bound to the function context and that this usage is closely related to the fundamental design of the REBOL. Brett.

 [9/40] from: lmecir:mbox:vol:cz at: 24-Apr-2002 15:10

Re: local vars in functions


Hi Rishi, <Rishi> One part of REBOL that can feel odd is when i have to define local variables of a function in the first block of the function. I find it a bit annoying to have to add new variables to the list for every little local variable I use. In addition, I feel it clutters up the first block and increases function size. In addition, the way it is currently done could make it easy to create hard to detect bugs. What I would like to see is another shortcut to creating local variables in any context (function, innerfunction, loop). The obvious way I see of doing this is as follows: myfunc: func [][ localvar:: $25 myinnerfunc: func [][ innerlocal:: $10 print localvar ; prints $25 ] print innerlocal; error! ] print localvar ; error! using the "::" for local var will make it more convienient to create local vars (which i use all the time over global vars). In addition, it will help prevent some errors of accidental global var creation because it is now easy to spot a local var. Best of all, this type of shortcut would not break anything in rebol. You could even use this in a loop: for count 1 10 1 [ localvar:: "hello" ] Using the "::" shortcut in a global context would be the same as using a ":". The disadvantage I see is that it adds another thing to the language.. But consider that now we could stop using the /local keyword, reduce bugs, and use it consistently everywhere, overall it can simplify things. Anybody have other reasons as to why it was not done this way?? Perhaps there is a performance issue?? </Rishi> Agreed, I tried to discuss and suggest that some time ago too. This needs a more complicated interpreter to run the code at least as quickly (or faster) as the current interpreter does.

 [10/40] from: rotenca:telvia:it at: 24-Apr-2002 15:01


>myfunc: func [][ > localvar:: $25
<<quoted lines omitted: 10>>
> prevent some errors of accidental global var creation > because it is now easy to spot a local var. Best of
You can already do something like: myfunc: func [][ context [ localvar: $25 myinnerfunc: func [][ context [ innerlocal: $10 print localvar ; prints $25 ] ] print innerlocal; error! ] ] print localvar ; error! --- Ciao Romano

 [11/40] from: joel:neely:fedex at: 24-Apr-2002 7:52


Hi, Rishi, My uninformed $0.02... (Note that I try to make an alternative suggestion at the end of my discussion. I'd be interested in feedback on that idea!) The main root problem I have with your suggestion is that it seems to make an assumption about REBOL that I do not believe to be true -- that there's some sort of conventional "compile" step that preprocesses the entire text of a piece of code before generating some internal representation. For why I think this, see below. Please let me know if I have misunderstood your meaning... Rishi Oswal wrote:
...
> What I would like to see is another shortcut to > creating local variables in any context (function,
<<quoted lines omitted: 9>>
> ] > print localvar ; error!
Based on your description, should I infer that you would like the above to be equivalent to? myfunc: func [/local localvar myinnerfunc] [ localvar: $25 myinnerfunc: func [/local innerlocal] [ innerlocal: $10 print localvar ] print innerlocal ; error ] print localvar ; error Note that I assumed you meant for MYINNERFUNC to be local to MYFUNC even though you didn't use the double colon (because of the way you named it). Do you intend the above meaning or do you intend an alternative meaning, such as: myfunc: func [] [ use [localvar] [ localvar: $25 use [myinnerfunc] [ myinnerfunc: func [] [ use [innerlocal] [ innerlocal: $10 print localvar ] ] print innerlocal ; error ] ] ] print localvar ; error Alternately, I could pose it as two questions: 1) What context do you think should contain the double-colon variables? 1a) The context of the smallest enclosing function definition? 1b) A new context whose scope is described below? 2) How far should the scope of that context extend (lexically I mean)? 2a) From just before the double-colon expression to the end of the enclosing block? 2b) From the beginning of the enclosing block to the end of the enclosing block? 2c) Just the following expression?
> using the "::" for local var will make it more > convienient to create local vars (which i use all the > time over global vars). In addition, it will help > prevent some errors of accidental global var creation > because it is now easy to spot a local var. >
Pardon me, but I must disagree. The typographic differences among the following expressions foo: 10 bletch:: "this is a test" somelongervariablename: foo + length? bletch shortername:: somelongervariablename + 5 * (thing1: square-root 75) - (thing2:: now/time/second) lastone: shortername + 7 do *not* IMHO make the local vs. non-local names stand out all that much.
> Best of > all, this type of shortcut would not break anything in > rebol. You could even use this in a loop: > > for count 1 10 1 [ > localvar:: "hello" > ] >
Based on this example, I assume you *must* mean (1b) is the context you want, but I'm still unclear what the answer to (2) would be. I'm also not clear on what semantics you intend e.g. for the following case myfunc1: func [s [string!]] [ a:: 1 foreach char s [ a:: a + 1 if all [#"A" <= char char <= #"Z"] [ a: a + 1 ] if all [#"a" <= char char <= #"z"] [ a:: a + 1 ] if all [#"0" <= char char <= #"9"] [ a: a + 1 ] ] a ]
> Using the "::" shortcut in a global context would be > the same as using a ":". >
So "::" behaves differently depending on "context" (I assume you mean lexical setting, not REBOL "context"...) and on which time it is being evaluated??? How does it know? (See below.) That's not the same as defining e.g. print flarp:: square-root length? "Yowp!" as meaning the same as use [flarp] [ print flarp: square-root length? "Yowp!" ] Remember that LOADing an expression implicitly wraps it in a block. All of the answers to (2) above have to do with the idea of the lexically-encl0sing block. Being able to create, use, and discard "temporary" variables at the global level is quite handy!
> The disadvantage I see is that it adds another thing > to the language.. But consider that now we could stop > using the /local keyword, reduce bugs, and use it > consistently everywhere, overall it can simplify > things. >
But it's *NOT* consistent, IMHO! In the example you give for count 1 10 1 [ localvar:: "hello" ] the double-colon-word LOCALVAR does something different the first time it is evaluated compared to the subsequent time(s) it is evaluated, although I'm not at all clear what that something is. You also seem to be saying that it must be able to figure out (dynamically, remember!) which of an arbitrarily large number of possible contexts should be affected. Remember that REBOL has no idea of "current context" so in the case (combining your examples) of func1: func [n [integer!]] [ func2: func [] [ for count 1 n 1 [ localvar:: count * count ] ] ] I must again wonder what context LOCALVAR belongs in, and how the reader (interpreter or human!) should figure that out, whereas in current REBOL there are four distinct cases, each of which is clear from the text of the expressions: ;; localvar belongs in the context of func1 func1: func [n [integer!]/local localvar] [ func2: func [] [ for count 1 n 1 [ localvar: count * count ] ] ] ;; localvar belongs in the context of func2 func1: func [n [integer!]] [ func2: func [/local localvar] [ for count 1 n 1 [ localvar: count * count ] ] ] ;; localvar belongs in a context inside the FOR body func1: func [n [integer!]] [ func2: func [] [ for count 1 n 1 [ use [localvar] [ localvar: count * count ] ] ] ] ;; localvar belongs in some other context established ;; outside this text fragment func1: func [n [integer!]] [ func2: func [] [ for count 1 n 1 [ localvar: count * count ] ] ] Because of the way REBOL views words and contexts, I have real trouble figuring out the interactions of this proposed new feature with all of the mechanisms already in the language. There are already enough cases where there are subtle differences and undocumented behaviors that I really can't feel good about adding another one. Finally, in a design situation I always like to ask "What problem are we trying to solve?" In this case it appears that the primary problem is the risk of modifying the global context by forgetting to declare a word as local to some scope under construction. If that's the case, I'd like to suggest an alternative. If I understand correctly, REBOL builds contexts as required by the expressions being evaluated (e.g., FUNC, USE, FOR, MAKE OBJECT!, etc.) and refuses to extend those contexts after initial creation -- with one specific exception being the global context, which may be extended at any time. What if REBOL had a new word, e.g. GLOBAL-CONTEXT-FROZEN (and I'm quite happy for suggestions for a better name!), that controlled whether adding new words to the global context were prohibited? Then we could write things like this: global-context-frozen: true do %somescript.r global-context-frozen: false and any attempt to add a word to the global context during the evaluation of the script file would cause an error. In addition, one could "nest" such protection e.g. within a script file such as somescript.r by writing: use [temp-global-safe] [ temp-global-safe: global-context-frozen global-context-frozen: true ;; do a bunch of stuff ;; ... global-context-frozen: temp-global-safe ] thus transparently ensuring that the "bunch of stuff" is evaluated without adding globals, and then restoring the level of safety that was in place when the above fragment began. (Of course, I'm avoiding the issue of whether we would get benefit from being able to "thaw" other contexts, e.g. to be able to add attributes to an existing object... ;-) -jn- -- ; Joel Neely joeldotneelyatfedexdotcom REBOL [] do [ do func [s] [ foreach [a b] s [prin b] ] sort/skip do function [s] [t] [ t: "" foreach [a b] s [repend t [b a]] t ] { | e s m!zauafBpcvekexEohthjJakwLrngohOqrlryRnsctdtiub} 2 ]

 [12/40] from: greggirwin:mindspring at: 24-Apr-2002 9:41


Hi Joel, << What if REBOL had a new word, e.g. GLOBAL-CONTEXT-FROZEN (and I'm quite happy for suggestions for a better name!), that controlled whether adding new words to the global context were prohibited? >> Given the discussion a short while ago about people wanting the ability to extend *any* context easily, what if you could do the following: UNFREEZE context extend context FREEZE context FROZEN? context <can't extend it if frozen> I don't how they might work, if it's feasible, or if it's even a good idea, but it's a thought. --Gregg

 [13/40] from: mat:0wnerage at: 24-Apr-2002 18:12


Hello Gregg, GI> UNFREEZE context GI> extend context GI> FREEZE context Actually it would have felt a bit more intuitive for me to actually specify variables that were global, and not mention variables that are local specifically. If only because the way I code that would have a hell of a lot less behind /local :) Anyhow, I appreciate this isn't going to happen and it's no big deal. Regards, Mat Bettinson

 [14/40] from: rishioswal:yaho:o at: 24-Apr-2002 12:18


--- Joel Neely <[joel--neely--fedex--com]> wrote:
> Hi, Rishi, > My uninformed $0.02...
<<quoted lines omitted: 46>>
> ] > print localvar ; error
hi joel. the code i meant to write is shown below. I should have made myinnerfunc end with "::". This code below should be exactly identical to the code you wrote above. Looks like you have thought more deeply into the problem than I have and I will take a look at it tonight - rough schedule today :) myfunc: func [] [ localvar:: $25 myinnerfunc:: func [] [ innerlocal:: $10 print localvar ] print innerlocal ; error ] print localvar ; error Note that I assumed you meant for MYINNERFUNC to be
> local to > MYFUNC even though you didn't use the double colon
<<quoted lines omitted: 24>>
> function > definition?
yes.
> 1b) A new context whose scope is described > below?
<<quoted lines omitted: 83>>
> Remember that LOADing an expression implicitly wraps > it in
=== message truncated ===

 [15/40] from: rishioswal:yah:oo at: 24-Apr-2002 12:04


--- Carl Read wrote:
> I like the idea and the syntax, but what happens > with the likes of
<<quoted lines omitted: 7>>
> or global value, > or both?
Hi Carl R. here was my brainstorming... a: 10 this would produce an 'a in the global context a:: 20 this would produce an 'a in the local context. If we are in the global context, then they would be the same 'a. a: 30 if both of the above 'a were in the global context, then all three words are one and the same. Basically, in the global space, there is no difference in setting with a:: or a: My reasons for making the change in the language would be mostly due to convience in functions. * no need do declare local word in first block of a func or second block of a function * no need to pick up the mouse, scroll to the first block of a func, and add the local word and then scroll back and write your code (writing code will be more continious with ::) * less code and typing since there is no need to declare local words (okay..so there is hardly any saving but still...) *does not burden beginner. Beginner does not have to use or learn this syntax, but once they understand local vars, it is no harder to learn "::" than to learn the use of "/local" in the first block of a func. *Syntactically backwards compatible with rebol * can use in inner words also as i had shown in my previous example.. works n levels deep *can use in other things other than functions *perhaps gets rid of the need to know the other ways of creating locals ('use, /local, etc..) *easy for "me" to spot a local vs global variable *helps reduce the bug when accidental global var is created due to misspelling or forgetting to declare a word. And if accidental global is created due to accidental use of ":" vs "::", then it is easier to spot than looking at all the local vars and checking spelling..etc Things that I don't know * performance issues * implementation issues actually, I don't know a whole lot. Just throwing out an idea which "seems" to be very useful in the way I program. I am sure gurujee (indian way of referring to carl S. :) and others on the list can explain why or why not this is possible or useful. rishi

 [16/40] from: rishioswal::yahoo at: 24-Apr-2002 12:36


here is another possible brainstorming idea for int/adv users without changing things for beginners. REBOL [] globvar1: $1000 globvar2: $9999 myfunc: func [/global globvar1] [ localvar: $25 print globvar1 print globvar2 ; error myinnerfunc: func [/global globvar2] [ innerlocal: $10 print localvar print globvar1 print globvar2 ] print globvar2 ; error print innerlocal ; error ] print localvar ; error and if you do this, it makes no difference (backwards compatibility): REBOL [] globvar1: $1000 globvar2: $9999 myfunc: func [ /local localvar myinnerfunc /global globvar1 ] [ localvar: $25 print globvar1 print globvar2 ; error myinnerfunc: func [ /local innerlocal /global globvar2 ] [ innerlocal: $10 print localvar print globvar1 print globvar2 ] print innerlocal ; error print globvar2 ; error ] print localvar ; error but i lean towards the "::" thing. - rishi --- Gregg Irwin <[greggirwin--mindspring--com]> wrote:

 [17/40] from: holger:rebol at: 24-Apr-2002 14:53


On Tue, Apr 23, 2002 at 08:08:15PM -0700, Rishi Oswal wrote:
> One part of REBOL that can feel odd is when i have to > define local variables of a function in the first
<<quoted lines omitted: 4>>
> addition, the way it is currently done could make it > easy to create hard to detect bugs.
It is the result of REBOL being an interpreter, not a compiler. A compiler examines the complete body of a function before generating code, so the variable declaration can be anywhere. For an interpreter there are only two options: 1. Make the context of a function "static", i.e. defined when a function is declared. In that case declarations have to be explicit, i.e. during the execution of "func" or "function", when the function is defined, all variables must be known. If declarations can appear in the body of a function then there is no way for "func" to know a list of all variables. Scanning the function body is not an option, because declarations could be "hidden", e.g. with something like [do rejoin ["a" ": " "1"]], i.e. there is no way for "func" to know about all declarations without actually executing the function. 2. Make the context of a function "dynamic", i.e. make it empty when a function is declared, and extend it at run-time as needed. This is not how REBOL currently works. Allowing this kind of model would require significant changes to the interpreter. It is the same problem as "extending" the context of objects.
> What I would like to see is another shortcut to > creating local variables in any context (function, > innerfunction, loop). The obvious way I see of doing > this is as follows: > > myfunc: func [][ > localvar:: $25
Nooooo. Please do not do that :-). You did not just create a notation with that, you actually created a new datatype: a set-set-word!. We do not like to create new datatypes unless it is really necessary and the exact meaning, scope, usefulness etc. are very well defined and can be clearly demonstrated. Creating a new datatype only to shortcut variable declaration seems ... inappropriate, for lack of a better word :-). -- Holger Kruse [kruse--nordicglobal--com]

 [18/40] from: greggirwin:mindspring at: 24-Apr-2002 21:05


Hi Rishi, << *Syntactically backwards compatible with rebol >> I'm relatively new to REBOL (8 months or so now) but I've learned that Carl and Holger think about these kinds of things very carefully. As Carl has said, lexical space is very tight in REBOL because of the high number of native datatypes and lack of sytactical artifice. It's always good to toss ideas around though. You never know what might happen. --Gregg

 [19/40] from: lmecir:mbox:vol:cz at: 25-Apr-2002 9:50


Hi, just a note: you would have to define print & func as global too for your code to work (that is why this isn't a good alternative). I once suggested another possibility: what if every set-word automatically declared the word as local, while other word would be local by default (which is usual in objects currently)? (See http://www.rebolforces.com/~ladislav/rep.html which contains some other ideas how to enhance the detectability of global vs. local variable errors) <rishi> here is another possible brainstorming idea for int/adv users without changing things for beginners. REBOL [] globvar1: $1000 globvar2: $9999 myfunc: func [/global globvar1] [ localvar: $25 print globvar1 print globvar2 ; error myinnerfunc: func [/global globvar2] [ innerlocal: $10 print localvar print globvar1 print globvar2 ] print globvar2 ; error print innerlocal ; error ] print localvar ; error and if you do this, it makes no difference (backwards compatibility): REBOL [] globvar1: $1000 globvar2: $9999 myfunc: func [ /local localvar myinnerfunc /global globvar1 ] [ localvar: $25 print globvar1 print globvar2 ; error myinnerfunc: func [ /local innerlocal /global globvar2 ] [ innerlocal: $10 print localvar print globvar1 print globvar2 ] print innerlocal ; error print globvar2 ; error ] print localvar ; error but i lean towards the "::" thing. - rishi

 [20/40] from: rebol665:ifrance at: 25-Apr-2002 10:39


Hi Ladislav, 1. I am amazed to see such a long post from you (just kidding). 2. I am testing my new foxmail email reader. Patrick

 [21/40] from: lmecir:mbox:vol:cz at: 25-Apr-2002 10:17


Hi, <Holger> It is the result of REBOL being an interpreter, not a compiler. A compiler examines the complete body of a function before generating code, so the variable declaration can be anywhere. For an interpreter there are only two options: 1. Make the context of a function "static", i.e. defined when a function is declared. In that case declarations have to be explicit, i.e. during the execution of "func" or "function", when the function is defined, all variables must be known. If declarations can appear in the body of a function then there is no way for "func" to know a list of all variables. Scanning the function body is not an option, because declarations could be "hidden", e.g. with something like [do rejoin ["a" ": " "1"]], i.e. there is no way for "func" to know about all declarations without actually executing the function. </Holger> Although that sounds logical, it is not the whole truth. Compare the way how USE declares the local variables (the declarations don't appear in the BODY argument) vs. the way how CONTEXT declares the local variables (the declararions appear in the BLK argument). That surely isn't a non-option. <Holger> 2. Make the context of a function "dynamic", i.e. make it empty when a function is declared, and extend it at run-time as needed. This is not how REBOL currently works. Allowing this kind of model would require significant changes to the interpreter. It is the same problem as "extending" the context of objects. </Holger> I am not a member of the "Object Extenders Club", nevertheless, the dynamic function context surely *is* useful sometimes. Such functions (slower than their native counterparts could be) we can create in Rebol even now. <Holger>
> What I would like to see is another shortcut to > creating local variables in any context (function, > innerfunction, loop). The obvious way I see of doing > this is as follows: > > myfunc: func [][ > localvar:: $25
Nooooo. Please do not do that :-). You did not just create a notation with that, you actually created a new datatype: a set-set-word!. We do not like to create new datatypes unless it is really necessary and the exact meaning, scope, usefulness etc. are very well defined and can be clearly demonstrated. Creating a new datatype only to shortcut variable declaration seems ... inappropriate, for lack of a better word :-). -- Holger Kruse [kruse--nordicglobal--com] -- </Holger> I think, that it can really be useful, although the proposal made by Rishi is not a complete thing.

 [22/40] from: holger:rebol at: 25-Apr-2002 11:20


On Thu, Apr 25, 2002 at 10:17:38AM +0200, Ladislav Mecir wrote:
> </Holger> > > Although that sounds logical, it is not the whole truth. Compare the way how > USE declares the local variables (the declarations don't appear in the BODY > argument) vs. the way how CONTEXT declares the local variables (the > declararions appear in the BLK argument). That surely isn't a non-option.
Yes, it is the whole truth. Read again: : Scanning the function : body is not an option, because declarations could be "hidden", e.g. with : something like [do rejoin ["a" ": " "1"]], i.e. there is no way for "func" : to : know about all declarations without actually executing the function. The only reason why 'context allows implicit declarations is because it executes its body. Functions obviously cannot do that at declaration time. -- Holger Kruse [kruse--nordicglobal--com]

 [23/40] from: lmecir:mbox:vol:cz at: 27-Apr-2002 8:39


Hi, (resending my yesterday's mail) I don't think, that the difference between CONTEXT and FUNC is big. First of all, I think, that it is a matter of preferences. To demonstrate, that the difference is really small, let's have a look at the following code sample: blk: [change at blk 9 first [b:] a: 1 placeholder 2] c: context blk probe blk probe c Here we see, that the BLK ends up being changed to: == [change at blk 9 first [b:] a: 1 b: 2] Which would yield a different result than make object! [ a: 1 ] when used as the BLK argument. My observation is, that the BLK argument is scanned for local words *before* it is being executed. To demonstrate a possibility to do something like that for functions, here is a LOCFUNC function (using some code from http://www.rebolforces.com/~ladislav/contexts.html ): locfunc: function [ {Define a function with set-words handled implicitly as local.} spec [block!] body [block!] "The body block of the function" ] [vars sw] [ vars: copy [] parse body [ any [ set sw set-word! (append vars to word! :sw) | skip ] ] make function! head insert insert tail copy spec /local vars body ] , which may be of some use for Rishi, n' est ce pas? Usage: a: 4 f: locfunc [] [a: 2] f ; == 2 a ; == 4 Cheers L ----- Original Message ----- From: "Holger Kruse" On Thu, Apr 25, 2002 at 10:17:38AM +0200, Ladislav Mecir wrote:
> </Holger> > > Although that sounds logical, it is not the whole truth. Compare the way
how
> USE declares the local variables (the declarations don't appear in the
BODY
> argument) vs. the way how CONTEXT declares the local variables (the > declararions appear in the BLK argument). That surely isn't a non-option.
Yes, it is the whole truth. Read again: : Scanning the function : body is not an option, because declarations could be "hidden", e.g. with : something like [do rejoin ["a" ": " "1"]], i.e. there is no way for "func" : to : know about all declarations without actually executing the function. The only reason why 'context allows implicit declarations is because it executes its body. Functions obviously cannot do that at declaration time. -- Holger Kruse [kruse--nordicglobal--com]

 [24/40] from: lmecir:mbox:vol:cz at: 26-Apr-2002 10:19


Hi, I don't think, that the difference between CONTEXT and FUNC is as clear as Holger states. First of all, I think, that it is a matter of preferences (which may differ). To demonstrate, that the difference is really small, let's have a look at the following code sample: blk: [change at blk 9 first [b:] a: 1 placeholder 2] c: context blk probe blk probe c Here we see, that the BLK ends up being changed to: == [change at blk 9 first [b:] a: 1 b: 2] Which would yield a different result than make object! [ a: 1 ] when used as the BLK argument. My observation is, that the BLK argument is scanned for local words *before* it is being executed. To demonstrate a possibility to do something like that for functions, here is a LOCFUNC function (using some code from http://www.rebolforces.com/~ladislav/contexts.html ): locfunc: function [ "Defines a user function with implicit local words." spec [block!] body [block!] "The body block of the function" ] [vars sw] [ vars: copy [] parse body [ any [ set sw set-word! (append vars to word! :sw) | skip ] ] make function! head insert insert tail copy spec /local vars body ] , which may be of some use for Rishi n' est ce pas? Usage: a: 4 f: locfunc [] [a: 2] f ; == 2 a ; == 4 Cheers L ----- Original Message ----- From: "Holger Kruse" On Thu, Apr 25, 2002 at 10:17:38AM +0200, Ladislav Mecir wrote:
> </Holger> > > Although that sounds logical, it is not the whole truth. Compare the way
how
> USE declares the local variables (the declarations don't appear in the
BODY
> argument) vs. the way how CONTEXT declares the local variables (the > declararions appear in the BLK argument). That surely isn't a non-option.
Yes, it is the whole truth. Read again: : Scanning the function : body is not an option, because declarations could be "hidden", e.g. with : something like [do rejoin ["a" ": " "1"]], i.e. there is no way for "func" : to : know about all declarations without actually executing the function. The only reason why 'context allows implicit declarations is because it executes its body. Functions obviously cannot do that at declaration time. -- Holger Kruse [kruse--nordicglobal--com]

 [25/40] from: anton:lexicon at: 1-May-2002 3:12


Ladislav, I disagree. I don't think this is an adequate proof. (You didn't observe the scan happening, so it's a theory, not an observation.) In my understanding, context evaluates its blk argument, and as it moves along from item to item, finds set-words and their values and adds them to the new object that it is creating. I would say the BLK argument is scanned for local words *as* it is being executed. Forgive me if I misunderstood. It's not terribly clear. Regarding your final statement at bottom, would you say that the following statements are true? 1) "BLK ... scanned for local words" -means-> "BLK is reduced" 2) "... executed" -means-> "made into an object" Anton.

 [26/40] from: g:santilli:tiscalinet:it at: 30-Apr-2002 20:52


Hi Anton, On Tuesday, April 30, 2002, 7:12:29 PM, you wrote: A> Ladislav, I disagree. I agree with Ladislav. A> I don't think this is an adequate proof. A> (You didn't observe the scan happening, A> so it's a theory, not an observation.) If you can find something that that theory cannot explain, the the theory is wrong. If not, it is an acceptable model. A> In my understanding, context evaluates its blk A> argument, and as it moves along from item to item, This, instead, can be proven wrong, and Ladislav just did in his last post. So it's not an adequate model to explain REBOL. I'm also sure Holger would agree with Ladislav, once the misunderstanding will be cleared. Regards, Gabriele. -- Gabriele Santilli <[g--santilli--tiscalinet--it]> -- REBOL Programmer Amigan -- AGI L'Aquila -- REB: http://web.tiscali.it/rebol/index.r

 [27/40] from: lmecir:mbox:vol:cz at: 30-Apr-2002 23:03


Hi Anton, a little discussion may be useful sometimes: <Anton> Ladislav, I disagree. I don't think this is an adequate proof. (You didn't observe the scan happening, so it's a theory, not an observation.) </Anton> You are right. It is a theory supported by observation. In http://www.rebolforces.com/~ladislav/contexts.html (in the MAKE OBJECT! section) I wrote a Rebol model of the Rebol interpreter behaviour during the MAKE OBJECT! evaluation. It has proven itself to be accurate, because it not only described the observed behaviour but it even allowed me to predict correctly the behaviour of the interpreter in complicated cases I haven't tried before. The theory is therefore proven as accurately as any theory I accept to be proven. <Anton> In my understanding, context evaluates its blk argument, and as it moves along from item to item, finds set-words and their values and adds them to the new object that it is creating. I would say the BLK argument is scanned for local words *as* it is being executed. </Anton> Your hypothesis can be easily falsified, if we compare its prediction with the result of the sample code: blk: [change at blk 9 first [b:] a: 1 placeholder 2] c: context blk probe blk probe c I think, that we are in agreement, that after evaluating the first part of BLK, BLK changes to: [change at blk 9 first [b:] a: 1 b: 2] *before* the 7-th element of BLK is evaluated. That means, that when the 9-th element of BLK is evaluated, the interpreter evaluates a set-word b: instead of the word 'placeholder. At the same time, we can easily find out, that if we instead do: blk: [change at blk 9 first [b:] a: 1 b: 2] c: context blk probe blk probe c we obtain a different result. <Anton> Forgive me if I misunderstood. It's not terribly clear. Regarding your final statement at bottom, would you say that the following statements are true? 1) "BLK ... scanned for local words" -means-> "BLK is reduced" 2) "... executed" -means-> "made into an object" Anton. </Anton> It looks, that you understood me well. I would say that neither 1) nor 2) is true. To be totally accurate, I would have to write the model code mentioned above that you can find in contexts.html Feel free to ask if anything remains unanswered. Cheers L

 [28/40] from: rotenca:telvia:it at: 1-May-2002 0:03


Hi, Anton, I agree with Ladislav (BTW he created, i think, a "perfect" simulation of make object! in one of his articles). As you can see here:
>> a: 1
== 1
>> context [
print unset? a ;(1) a: "b" ;(2) ] true the word 'a in the print statement in the block context (1) is unset: it must be local, because the global 'a is = 1. Being unset, it is not already initialized with "b", and the expression (2) has not been executed, but the interpreter already "knows" that the word 'a is local to the block context. Conclusion: before executing the block context, all the set-words are found in the upper level block, a new context build, all its values set to unset! and the block bound to the new context. Only at this time the block is executed. This could be delayed easy, like in a function, without changing the context work. Another example will prove that an object can be created without executing all its block. Indeed is a "monster" object for a bug in Rebol (someone has reported it to feedback?):
>> x: does [context [return 1 a: 2 b: 3]] >> print type? o: x
object
>> print type? o/a
unset
>> print type? o/b
unset I call It a "monster" because this object fails in many manners: if this is standard:
>> first o
== [self a b] this is not standard:
>> probe o
make object! [ a: end ;?????? b: unset ] neither this:
>> second o
== [ make object! [ a: end b: unset ]] and this:
>> third o
== [a:] --- Ciao Romano

 [29/40] from: rotenca:telvia:it at: 1-May-2002 3:45


Hi, Rethinking at my previous message, i must change one thing. At the start of the block context, the first (set)word is a strange word which i don't understand well. Could it be a bug? This is OK: foo: 0 foo2: 1
>> make object! [get 'foo2 foo: 2 foo2: 3]
** Script Error: foo2 has no value ** Near: get 'foo2 foo: 2 foo2: But not this :
>> make object! [get 'foo foo: 2 foo2: 3]
why not the error? This is OK:
>> make object! [print type? get/any 'foo2 foo: 2 foo2: 3]
unset But not this:
>> make object! [print type? get/any 'foo foo: 2 foo2: 3]
** Script Error: type? expected value argument of type: any-type ** Near: print type? get/any 'foo foo: What happens? What type of word is it? Seems an unbound word, but it does not give the standard error message of an unbound word (if i am not wrong, Ladislav's undefined? func returns true for 'foo but the error catched by the func is not the standard "not defined in this context") --- Ciao Romano

 [30/40] from: rotenca:telvia:it at: 1-May-2002 3:55


Hi, because my example in the first message used the first "strange-buggy" word of context, it was wrong, a correct version could be: a: b: 1 make object! [print value? 'b a: "" b: ""] false which confirms that 'b is local and unset. --- Ciao Romano

 [31/40] from: lmecir:mbox:vol:cz at: 1-May-2002 9:01


Hi Romano, nice code samples. I can say that you revealed some bugs in the Rebol interpreter behaviour as well as in my simulator. To obtain accurate results I would have to modify my simulator as follows: blank-object: function [ {make a blank object} set-words [block!] ] [object] [ set-words: compose [return self (set-words)] object: do does [ make object! set-words ] set/any in object 'self () object ] Another inaccuracy discovered by you is in the definition of my UNBOUND? function. As you found out, it yields: o: blank-object [a:] word: in o 'a unbound? word ; == true A correct UNBOUND? function should have looked like this: unbound?: function [ {determines, if a word is unbound} word [any-word!] ] [err] [ found? all [ error? err: try [any-type? get/any :word] err: disarm err equal? err/id 'not-defined ] ] Now we get: unbound? word ; == false unbound? second first o ; == true Cheers L ----- Original Message ----- From: "Romano Paolo Tenca" Hi, Rethinking at my previous message, i must change one thing. At the start of the block context, the first (set)word is a strange word which i don't understand well. Could it be a bug? This is OK: foo: 0 foo2: 1
>> make object! [get 'foo2 foo: 2 foo2: 3]
** Script Error: foo2 has no value ** Near: get 'foo2 foo: 2 foo2: But not this :
>> make object! [get 'foo foo: 2 foo2: 3]
why not the error? This is OK:
>> make object! [print type? get/any 'foo2 foo: 2 foo2: 3]
unset But not this:
>> make object! [print type? get/any 'foo foo: 2 foo2: 3]
** Script Error: type? expected value argument of type: any-type ** Near: print type? get/any 'foo foo: What happens? What type of word is it? Seems an unbound word, but it does not give the standard error message of an unbound word (if i am not wrong, Ladislav's undefined? func returns true for 'foo but the error catched by the func is not the standard "not defined in this context") --- Ciao Romano

 [32/40] from: rotenca:telvia:it at: 1-May-2002 12:36


Hi Ladislav,
> blank-object: function [ > {make a blank object}
<<quoted lines omitted: 7>>
> object > ]
why not: blank-object: function [ {make a blank object} set-words [block!] ] [object][ unset in object: context compose [exit (set-words)] 'self object ] not so readable, i must admit (and 'object could be replaced by 'set-words to make it more unreadable): blank-object: func [ {make a blank object from a block of set-words} blk [block!] ] [ unset in blk: context compose [exit (blk)] 'self blk ]
> unbound?: function [ > {determines, if a word is unbound}
<<quoted lines omitted: 6>>
> ] > ]
If equal? err/id 'not-defined is false, the function returns false, but the status of the word is unknown, so i think it should be better to fire an error! --- Ciao Romano

 [33/40] from: rotenca:telvia:it at: 1-May-2002 14:48


Hi Ladislav, looking at your sim-make-object!: function [ {MAKE OBJECT! simulation} [throw] spec [block!] ] [set-words object sw] [ ; find all set-words in SPEC set-words: copy [] parse spec [ any [ copy sw set-word! (append set-words sw) | skip ] ] ; create a blank object object: blank-object set-words ; set 'self in object to refer to the object object/self: object ; bind the SPEC to the blank object spec: bind spec in object 'self do spec ; return the value of 'self as the result get/any in object 'self ] I think it needs a correction: do spec must become make object! spec or, to simulate only its side effects: catch [loop 1 [do does spec]] --- Ciao Romano

 [34/40] from: lmecir:mbox:vol:cz at: 1-May-2002 15:16


Hi Romano, you got me again! My models are unable to demonstrate the behaviour your code relies on: <Romano> ... not so readable, i must admit (and 'object could be replaced by 'set-words to make it more unreadable): blank-object: func [ {make a blank object from a block of set-words} blk [block!] ] [ unset in blk: context compose [exit (blk)] 'self blk ] </Romano> :-o I would have guessed, that the only way how to do that would be: blank-object: func [ {make a blank object} blk [block!] ] [ unset in blk: context compose [return self (blk)] 'self blk ] It is a mistery for me, why exit works in this case.
> unbound?: function [ > {determines, if a word is unbound}
<<quoted lines omitted: 6>>
> ] > ]
<Romano> If equal? err/id 'not-defined is false, the function returns false, but the status of the word is unknown, so i think it should be better to fire an error! --- Ciao Romano </Romano> My goal was to find out, whether the examined word is unbound or not. I am pretty sure, that the word in question is unbound only if the error id is 'not-defined. In the above case the source of the error isn't the GET/ANY expression, but the ANY-TYPE? function, which has nothing to do with the binding of the examined word. (the examined word is clearly bound to the created object) Nevertheless, it is a shocking experience, that there is a Rebol value which isn't acceptable for the ANY-TYPE? function. I suggest you to summarize your findings and send them all to feedback.

 [35/40] from: anton:lexicon at: 2-May-2002 1:26


Ladislav, I now understand your point, thanks. :) Anton.

 [36/40] from: rotenca:telvia:it at: 1-May-2002 17:34


Hi Ladislav, c> unset in blk: context compose [exit (blk)] 'self
> blk > ]
<<quoted lines omitted: 8>>
> ] > It is a mistery for me, why exit works in this case.
I do not think, i learned it from you :-) An object always return the self value whatever the code executed will return. Every return value is replaced by the value of 'self. Return 1, for example, make the context block returns an integer, but the make object! returns always the self value. So you can something like:
>> type? make object! [self: "string" a: 1]
== string!
>> type? make object! [set 'o self self: "string" a: 1]
== string!
>> probe o
make object! [ a: 1 ]
>From the last example it seems to me that 'self is set to the object! before
any execution, and changed at the end (for your sim-object!). It seems it is never unset! So blank-object could be more simple: blank-object: func [ {make a blank object} blk [block!] ] [ context compose [exit (blk)] ] You can do also more complex things:
>> type? f: make object! [self: does [print "I am a function" print ["a =" a]]
a: 1] == function! ; the body of which is bound to an hidden context
>> a: 2
== 2
>> f
I am a function a = 1 and so on. You can also use throw or break to stop the block execution:
>> probe make object! [break a: 1]
make object! [ a: end ]
>> probe make object! [throw "" a: 1]
make object! [ a: end ] So to emulate it you must catch return, exit, break and throw. The only exception is make error!, in this case, the object creation is stopped, the object is not created and the error returned, not the self value (of an unexisting object :-).
> My goal was to find out, whether the examined word is unbound or not. I am > pretty sure, that the word in question is unbound only if the error id is > 'not-defined. In the above case the source of the error isn't the GET/ANY > expression, but the ANY-TYPE? function, which has nothing to do with the > binding of the examined word. (the examined word is clearly bound to the > created object)
I think you are right.
> Nevertheless, it is a shocking experience, that there is a Rebol value which > isn't acceptable for the ANY-TYPE? function.
Perhaps we have found the only true unset word in Rebol, the others have always a value: unset!.
>> o: make object! [break a: 1] >> get in o 'a
we do not need get/any !! But:
>> type? get in o 'a
** Script Error: type? expected value argument of type: any-type ** Near: type? get in o 'a the value of a is not of any-type!, neither unset!, it has no no value (nothing! datatype :-). But, there is always a but:
>> type? o/a
== unset!
>> get o/a
** Script Error: get expected word argument of type: any-word ** Near: get o/a
> I suggest you to summarize your > findings and send them all to feedback.
Sent, but Feddback do not sent back any feedback :-), neither the ticket number, i have the sensation to send my message to the land of no return. Ciao Romano

 [37/40] from: lmecir:mbox:vol:cz at: 1-May-2002 17:44


Hi Romano, <Romano> Hi Ladislav, looking at your sim-make-object!: function [ {MAKE OBJECT! simulation} [throw] spec [block!] ] [set-words object sw] [ ; find all set-words in SPEC set-words: copy [] parse spec [ any [ copy sw set-word! (append set-words sw) | skip ] ] ; create a blank object object: blank-object set-words ; set 'self in object to refer to the object object/self: object ; bind the SPEC to the blank object spec: bind spec in object 'self do spec ; return the value of 'self as the result get/any in object 'self ] I think it needs a correction: do spec must become make object! spec </Romano> No, I think, that my code works. (Test it, please, and let me know if anything doesn't work as it should). <Romano> or, to simulate only its side effects: catch [loop 1 [do does spec]] --- Ciao Romano </Romano> Why do you suggest this form? (I never wrote code looking like that - loop 1 or catch are unusual for me in this case) Cheers L

 [38/40] from: rotenca:telvia:it at: 1-May-2002 19:33


Hi Ladislav,
> No, I think, that my code works. (Test it, please, and let me know if > anything doesn't work as it should).
...
> Why do you suggest this form? (I never wrote code looking like that - loop 1 > or catch are unusual for me in this case)
For test, try the code at the end. The new version use the catch loop trick (the make object! spec solution was a joke: "definition must not contain the word defined", ipse dixit). In the previous message i tryed to explain the problem: any throw, return, exit, break is catched by make object!, your simulation, instead, execute them like normal instrutions. ------------- rebol [] ;a new version of blank-object blank-object: func [ {make a blank object} blk [block!] ] [ unset in blk: context compose [return self (blk)] 'self blk ] ;original sim-make-object: function [ {MAKE OBJECT! simulation} [throw] spec [block!] ] [set-words object sw] [ ; find all set-words in SPEC set-words: copy [] parse spec [ any [ copy sw set-word! (append set-words sw) | skip ] ] ; create a blank object object: blank-object set-words ; set 'self in object to refer to the object object/self: object ; bind the SPEC to the blank object spec: bind spec in object 'self do spec ; return the value of 'self as the result get/any in object 'self ] ;the patched sim-make new-sim-make-object: function [ {MAKE OBJECT! simulation} [throw] spec [block!] ] [blk object sw] [ blk: copy [exit] parse spec [ any [ copy sw set-word! (insert tail blk sw) | skip ] ] object: make object! blk catch [loop 1 [do does bind spec in object 'self]] get/any in object 'self ] ;the text code foreach blk [ [a: 1 exit] [b: 2 return] [c: 3 break] [d: 4 throw 1]] [ print ["--------" mold blk "---------"] ori: new: old: none ori: make object! copy blk new: new-sim-make-object copy blk loop 1 [ catch [ do does [ old: sim-make-object copy blk ] ] ] ?? ori ?? new ?? old ] halt --- Ciao Romano

 [39/40] from: lmecir:mbox:vol:cz at: 1-May-2002 21:34


Hi Romano, thank you, <Romano> Hi Ladislav, c> unset in blk: context compose [exit (blk)] 'self
> blk > ]
<<quoted lines omitted: 8>>
> ] > It is a mistery for me, why exit works in this case.
I do not think, i learned it from you :-) An object always return the self value whatever the code executed will return. Every return value is replaced by the value of 'self. </Romano> This looks *very* strange to me. (I wonder, what would Joel say to that?) <Romano> ... 'self is set to the object! before any execution, ... </Romano> Exactly that I wanted to underline. That is why I designed the sim-make-object! to set the word before the execution too. The blank object is simply a blank context, no mistery in it. <Romano> You can also use throw or break to stop the block execution:
>> probe make object! [break a: 1]
make object! [ a: end ]
>> probe make object! [throw "" a: 1]
make object! [ a: end ] So to emulate it you must catch return, exit, break and throw. The only exception is make error!, in this case, the object creation is stopped, the object is not created and the error returned, not the self value (of an unexisting object :-). </Romano> Now I see. I didn't expect any special properties of the kind you are describing, to be honest. I would suggest a more consistent behaviour WRT RETURN, EXIT, BREAK and THROW. (What would Joel say, again?). There is just one problem, then: the code will be less readable than it is! Nevertheless, I should replace the code as you suggested. Anyway (readability reasons) I will preserve the structure, where the MAKE function first creates a (totally) blank object and then sets the 'self word explicitly (to underline the fact, that 'self is really being set before the execution) etc...

 [40/40] from: lmecir:mbox:vol:cz at: 2-May-2002 19:39


Hi all, I updated the http://www.rebolforces.com/~ladislav/contexts.html as well as contexts.r at the same location to reflect the differences found by Romano. Thanks again, Romano. Cheers L

Notes
  • Quoted lines have been omitted from some messages.
    View the message alone to see the lines that have been omitted