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

WYSIWYG programming

 [1/32] from: lmecir:geocities at: 25-Oct-2000 12:22


Part #2 Use as a code modifying function: --------------------------------- original-code7: [ temporary: "temporary" ] code7: copy original-code7 use [temporary] code7 same? first original-code7 first code7 == false You can say: "So what? The Use behaviour is normal!" The problem is, that you can unintentionally write a Self Modifying Code with all its disadvantages, as the next code shows: f7: func [level] [ use [temporary] [ if level = 0 [ temporary: "temporary" f7 level + 1 print ["Temporary:" temporary] ] ] ] f7 0 ** Script Error: temporary has no value. ** Where: temporary Is there a way out? Yes there is, quite simple: f8: func [level] [ use [temporary] copy/deep [ if level = 0 [ temporary: "temporary" f8 level + 1 print ["Temporary:" temporary] ] ] ] f8 0 Temporary: temporary *The WYSIWYG rule*: If you use a modifying function don't allow it to modify the executed code, supply it a code copy instead. Another way to cope with the above trouble is to create a non-modifying Use, e.g. like this: orig-use: :use use: func [ "Defines words local to a block." words [block! word!] "Local word(s) to the block" body [block!] "Block to evaluate" ] [ orig-use words copy/deep body ] Make as a code modifying function: ----------------------------------- f9: func [level] [ make object! [ a: 2 * level b: either zero? level [ f9 1 ] [ none ] a: a + 1 ] ] probe f9 0 make object! [ a: 0 b: unset ] Once again, Make modified its second argument, which happened to be the code in execution. The way out is easy: f10: func [level] [ make object! copy/deep [ a: 2 * level b: either zero? level [ f10 1 ] [ none ] a: a + 1 ] ] probe f10 0 make object! [ a: 1 b: make object! [ a: 3 b: none ] ] Repeat as a code modifying function: ------------------------------------- Let's try a more complicated example. In the Set Theory the sets modelling integer numbers 0,1,2,3,... are created as follows: 0 is modelled by empty set, which is normally denoted by 0 too every other number is modelled as the set containing all models of integers smaller than the modelled integer, i.e. 1 is modelled by [0] 2 is modelled by [0 [0]] 3 is modelled by [0 [0] [0 [0]]] ... A Rebol function to do this could be written as: f11: function [n] [result] [ if n = 0 [ return 0 ] result: copy [] repeat i n copy/deep [ append/only result f11 i - 1 ] ]
>> f11 0
== 0
>> f11 1
== [0]
>> f11 2
== [0 [0]]
>> f11 3
== [0 [0] [0 [0]]]
>> f11 4
== [0 [0] [0 [0]] [0 [0] [0 [0]]]]
>> f11 5
== [0 [0] [0 [0]] [0 [0] [0 [0]]] [0 [0] [0 [0]] [0 [0] [0 [0]]]]] What would happen, if we allowed Repeat to modify the executed code? Let's see: f12: function [n] [result] [ if n = 0 [ return 0 ] result: copy [] repeat i n [ append/only result f12 i - 1 ] ]
>> f12 0
== 0
>> f12 1
== [0]
>> f12 2
== [0 [0]]
>> f12 3
== [0 [0] [0]] Forskip is a Modifying Function too. Next part will contain more WYSIWYG examples. -Ladislav

 [2/32] from: joel:neely:fedex at: 25-Oct-2000 8:19


Hi Ladislav, and list, Three brief points (don't die of shock! ;-) 1) Ladislav has done an excellent job of analysis and documentation of the kind of subtlety that can prove fatally threatening to a newcomer's interest in REBOL. His remarks should be read and studied by every REBOL programmer. 2) The current state of REBOL is beginning to remind me of the state of FORTH when I last used it seriously (mid-80s). One issue that hindered the growth and acceptance of FORTH was the fact that one had to understand most of its language-level concepts in terms of implementation details; to effectively use the language, one had to understand ALL of the way that ALL of the core was built. I'd hate to see REBOL suffer the same marginalization. 3) Using an "economic" perspective, one (oversimplified!) conclusion I reach from Ladislav's work is to say the following: To use REBOL control structures reliably, one MUST first understand the significance of copying, deep copying, function definition, and contexts. I worry about whether this cost is so high as to prove prohibitive to entry-level REBOL users. At the VERY LEAST, such issues must be documented with great clarity and completeness to avoid frustration on the part of new or not-so-new REBOL programmers. At the VERY BEST, such issues would be addressed by changes to the implementation/definition of the control structures involved; Ladislav has offered possible solutions, I'm sure there may be others. As always, my remarks are offered in the spirit of desiring VERY MUCH to see REBOL succeed. That desire motivates me to discuss those areas which I perceive as being threats to that success. -jn-

 [3/32] from: sharriff:aina:med-iq at: 25-Oct-2000 14:52


Very important thread, it is very helpful that so many people on the list take such matters into consideration. Its just strange that RT seems very silent about this issue... Sharriff Aina med.iq information & quality in healthcare AG Gutenbergstr. 42 Joel Neely <[joel--neely--f] An: [rebol-list--rebol--com] edex.com> Kopie: Gesendet von: Thema: [REBOL] Re: WYSIWYG programming rebol-bounce@ rebol.com 25.10.00 14:19 Bitte antworten an rebol-list Hi Ladislav, and list, Three brief points (don't die of shock! ;-) 1) Ladislav has done an excellent job of analysis and documentation of the kind of subtlety that can prove fatally threatening to a newcomer's interest in REBOL. His remarks should be read and studied by every REBOL programmer. 2) The current state of REBOL is beginning to remind me of the state of FORTH when I last used it seriously (mid-80s). One issue that hindered the growth and acceptance of FORTH was the fact that one had to understand most of its language-level concepts in terms of implementation details; to effectively use the language, one had to understand ALL of the way that ALL of the core was built. I'd hate to see REBOL suffer the same marginalization. 3) Using an "economic" perspective, one (oversimplified!) conclusion I reach from Ladislav's work is to say the following: To use REBOL control structures reliably, one MUST first understand the significance of copying, deep copying, function definition, and contexts. I worry about whether this cost is so high as to prove prohibitive to entry-level REBOL users. At the VERY LEAST, such issues must be documented with great clarity and completeness to avoid frustration on the part of new or not-so-new REBOL programmers. At the VERY BEST, such issues would be addressed by changes to the implementation/definition of the control structures involved; Ladislav has offered possible solutions, I'm sure there may be others. As always, my remarks are offered in the spirit of desiring VERY MUCH to see REBOL succeed. That desire motivates me to discuss those areas which I perceive as being threats to that success. -jn-

 [4/32] from: holger:rebol at: 25-Oct-2000 10:33


On Wed, Oct 25, 2000 at 02:52:45PM +0100, [Sharriff--Aina--med-iq--de] wrote:
> Very important thread, it is very helpful that so many people on the list > take such matters into consideration. Its just strange that RT seems very > silent about this issue...
As mentioned before, this is because we are extremely busy. I'll make an attempt at a response though. In any case, this should be considered my personal opinion only. It does not represent the official position of REBOL Technologies. Carl, e.g., is much more of an expert in programming languages than I am, and may see some things differently, or place different emphasis. First of all, I consider most of what Ladislav mentioned to be the result of switching pain , difficulties when switching from one language to another one, and adjusting to a different way of thinking. It is easy to keep using paradigms that are common in C or Pascal when switching to REBOL (paradigms such as "code", "variables", "execution" etc.), instead of switching to the REBOL way of thinking. This sometimes gets people into trouble. From the terminology Ladislav used I suspect that this may be the case here, too. See below. Switching pain is not unique to REBOL. It happens almost any time someone switches from any language to any other language. If you ever had to switch from Modula/Pascal to C you probably found that 'a:=""' does not simply become 'a=""', and that in C "if(a=1)" may not do what you want :-). It would be very nice to have "quick quides" such as "REBOL for C programmers", REBOL for Modula programmers or even "REBOL for PERL programmers" (ouch!), which briefly explained the major paradigm differences and specific things to watch out for, e.g. knowing that in REBOL "a: []" does NOT mean "copy an empty block to the variable 'a'". Or the fact that in REBOL arithmetic evaluation is left-to-right, without precedence rules. We may eventually provide such guides, but, you know, limited time... Maybe someone out in the community could write a first draft. I agree with Joel that, in order to become really adept at REBOL, you have to understand some of its concepts (values, references, NO variables !, blocks, contexts, words etc.), but then that is true for any language. Try programming in C without knowing what a pointer is, how to use it, what the difference between a global, a static, an auto or an extern declaration is, or what you need to do to handle strings (doing memory management yourself etc.). In comparison REBOL seems a lot easier, but even with REBOL, if you don't know what words, references, contexts or blocks are you will eventually run into road blocks (no pun intended). Interestingly enough, from our experience users who have never used other languages before often have FEWER problems adjusting to REBOL's way of thinking than experienced programmers. That's probably because experienced programmers tend to skip forewords and introductory chapters in manuals (the "I know that already" syndrome), and don't even realize until much later that they are stuck with a way of thinking that does not fully apply and that tends to make things more difficult for them. To me, this whole issue is about collecting information on what particular areas cause problems for users which backgrounds, and presenting/distributing this information in a way that makes it easier for users to adjust. Now about the particular discussion regarding "self-modifying code" (shudder), and a slight attempt at "re-education" :-). First of all, I appreciate Ladislav's attempt at explaining those "gotchas". I don't fully agree with his explanation or interpretation though :-), for several reasons. Ladislav seems to apply terms that are not really appropriate, such as "self-modifying code" (or even just "code"), "executed code", "code in execution" etc. This is not the REBOL way of thinking, and in some situations it may even make issues more difficult to understand. In REBOL everything is data, so you never have "self-modifying code". A block of data may get evaluated, and in the process even modified, but you start off with data and end up with data. This may seem like unnecessary emphasis on terminology (and I am usually not a stickler for terminology), but you will see the benefits in a minute... Part of the confusion results from a misunderstanding exactly what a: [] means. Some users may intuitively think that a ":" in REBOL is similar to a ":=" in Pascal/Modula, and that "a: []" means "create the variable 'a' and initialize it to an empty block". If they find that the same part of their script later reads a: ["text"] they may assume that something "modified their code", and that "if the statement is executed again the variable 'a' is now assigned to a different value". That is NOT the case. It is incorrect thinking that is leading to a misinterpretation of what is happening. Here is what a: [] really means: First keep in mind, that both the "a:" and the "[]" are within another block, i.e. they are just pieces of data. That means the "[]" is a block that exists before the function is even evaluated. The block was created when the script was first loaded, and at THAT time the block is created empty. When "a: []" is evaluated, a reference from the word "a" to the block following the a: is created. That is ALL that happens. No data is copied, no data is initialized. The effect is that, from that point on, evaluating the word "a" returns the block a is referencing, regardless of its value. That block starts off being empty, but, as with any other block, its value can change. The fact that the block is located inside of another block, which just happens to be a function body, makes no difference. REBOL does not enforce the block to be "constant". If you create a reference to it and then change the block through the reference -- then the block will get changed, even if it was defined inside of a function body. This is not "self-modifying code", but simply "data being modified", which is what programming is all about. The "code" is still the same. When evaluated it creates a reference from the word "a" to the block following the "a:" and it does that same thing every time. The contents of the block may have changed, but it is still the same block, so the evaluation still does the same thing and will always create the same reference. If you really want to compare this to something in other programming languages, then compare it to something like the following in C: char data[16]="abc"; void f(void) { char *a=data; printf("%s\n",a); strcat(a,"1"); } The first time you call the function it prints "abc", the next time "abc1", then abc11 etc., eventually it crashes :-). No self-modifying code here either. Observe how the "a=data" pointer assignment (somewhat similar to reference in REBOL, not 100% though) executes the exact same code every time the function is called. Not strange behavior at all. You could even put the "abc" into the function and make "a" static. Same effect. We need pointers and string manipulation here to "emulate" what REBOL does. That's because "variables" behave differently than "references".
> 2) The current state of REBOL is beginning to remind me of the state > of FORTH when I last used it seriously (mid-80s). One issue that
<<quoted lines omitted: 3>>
> to understand ALL of the way that ALL of the core was built. I'd > hate to see REBOL suffer the same marginalization.
I don't think users need to know any implementation details of REBOL. The main stumbling block seems to be a conceptual misunderstanding what a "reference" or value is, e.g. compared to a "variable". Knowing how REBOL internally represents a series or a word would not help.
> To use REBOL control structures reliably, one MUST first > understand the significance of copying, deep copying, function > definition, and contexts. I worry about whether this cost is > so high as to prove prohibitive to entry-level REBOL users.
Ladislav suggests deep copying everything. I don't think that is necessary or appropriate (from a performance point of view). Just as you do not want to fork() lots of processes in C only because there is a static variable somewhere in your code. You just need to think about what behavior you want, and what it means in terms of references to data items. Using the example code1: [ i: 0 while [(i: i + 1) <= 5] [ block: [] insert block "text" ] block ] Here "block: []" means "Create a reference to a block embedded in the function body." (the block following "block:"). At the end of the function "block" means evaluate block and return the result , which just happens to be a reference to a block embedded in the function body, because that is what you set the word "block" to. This may not be what you want though. Not only is evaluating "block: []" inside of the while loop redundant (because the reference never changes, so you can just as well do it only once, before the loop, making the code more readable and efficient), but you are also returning a reference to an item inside of your function. The caller would not be free to manipulate it without causing side effects, and different callers would get references to the SAME item every time. THAT is why you should use "copy". "block: copy []" really gives you a NEW empty block, not a reference to an existing block. It means "use the following (empty) block as a template to give me a reference to a new empty block". It is somewhat like the new operator in C++. If it makes things easier to understand, consider "copy []" to be a constructor for a new empty block. Same thing regarding empty strings (or any series in general). Also, always keep in mind that "a: []" creates a reference, but does not copy any data and does not create any new "object"/"entity". That kind of thinking should explain REBOL's behavior quite nicely, without resorting to wild copy/deep workarounds for "self-modifying code" :-). -- Holger Kruse [holger--rebol--com]

 [5/32] from: ryanc:iesco-dms at: 25-Oct-2000 11:32


First I would like to thank Holger for the very enlightening explanation. I am forever changed. --Ryan Holger Kruse wrote:
> On Wed, Oct 25, 2000 at 02:52:45PM +0100, [Sharriff--Aina--med-iq--de] wrote: > >
<<quoted lines omitted: 160>>
> [rebol-request--rebol--com] with "unsubscribe" in the > subject, without the quotes.
-- Ryan Cole Programmer Analyst www.iesco-dms.com 707-468-5400 I am enough of an artist to draw freely upon my imagination. Imagination is more important than knowledge. Knowledge is limited. Imagination encircles the world. -Einstein

 [6/32] from: lmecir:geocities at: 25-Oct-2000 22:46


Hi list, Joel wrote:
> 3) Using an "economic" perspective, one (oversimplified!) conclusion > I reach from Ladislav's work is to say the following: > > To use REBOL control structures reliably, one MUST first > understand the significance of copying, deep copying, function > definition, and contexts. I worry about whether this cost is > so high as to prove prohibitive to entry-level REBOL users. >
I don't think so. If I consider Rebol control structures like While, Either, If, Foreach, Loop,..., everything seems to be OK (I didn't include For just because it still contains a series bug I discovered/patched, but that bug has no influence on its execution in normal cases - i.e. when you don't use it to traverse a series). If anybody prefers a WYSIWYG Code, (s)he shall use improved Use I suggested in recursive functions, avoid to use Forskip and Repeat recursively (or patch them accordingly), be careful when using Make Object! recursively (or patch it too). The problem is, that sometimes you can't tell for sure, if a code isn't being executed recursively. In that case you should use the safe strategy. Holger wrote: First of all, I consider most of what Ladislav mentioned to be the result of switching pain , difficulties when switching from one language to another one, and adjusting to a different way of thinking. It is easy to keep using paradigms that are common in C or Pascal when switching to REBOL (paradigms such as "code", "variables", "execution" etc.), instead of switching to the REBOL way of thinking. This sometimes gets people into trouble. From the terminology Ladislav used I suspect that this may be the case here, too. See below... Ladislav: Not at all! My goal is to show the list members, and to myself, the difference between WYSIWYG and SM Rebol Code and to show you how and why to choose the one that you prefer. To make myself clear, I prefer the WYSIWYG Rebol Code, although I am not trying to write the definition of the notion, because it seems to me, that the examples are the best means to demonstrate the sense of the notion. (In the case you didn't notice, this is only a way how to hide, that I am not able to put together a definition right now ;-) Holger: Ladislav suggests deep copying everything. I don't think that is necessary or appropriate (from a performance point of view). Ladislav: I am afraid I didn't succeed to demonstrate (e.g. in Code3), that deep copying may be a nonsense sometimes, as long, as you don't know, what you are doing :-( Re code vs. data issue: I, being a programmer, recall, that every piece of code I wrote in any programming language, was written as data for appropriate program I used at that time... Back to Part #3: *WYSIWYG rule*: Don't try to modify anything that is at the same time being evaluated as Rebol code, otherwise be prepared to get a WYSINWYG/SM Code. WYSIWYG examples: First, I will try to show you how to convert: a: [append a [+ 1] 1] do a to a WYSIWYG Rebol Code. Here the problem is, that A is being modified at the same time A is being Done, which is what I mean by SMC. That fact really causes a strange result, as you might have seen in Part#1. The converted code: ; the first modification preserves just the line below and is almost unnecessary ; (as long, as the line isn't reused) a: copy [append a [+ 1] 1] ; the next modification is here to make sure we don't modify A and Do A at the same time do copy a The result is obvious, the above code works as expected! Now let's WYSIWYG-ize Code6: code6: [1 (remove code6) 2] do code6 Everybody can guess, what I would suggest: code13: copy [1 (remove code13) 2] do copy code13 == 2 At last! A more real-life example: counter: does [ 0 change second :counter (first second :counter) + 1 first second :counter ]
>> counter
== 1
>> counter
== 2
>> counter
== 3
>> counter
== 4
>> counter
== 5
>> counter
== 6
>> counter
== 7
>> counter
== 8 The above code does something useful. There is not an easy way to convert that to WYSIWYG, because the GC bug could mess things. The most obvious is: counter-memory: 0 counter: does [counter-memory: counter-memory + 1]

 [7/32] from: joel:neely:fedex at: 26-Oct-2000 22:25


Hello, Holger! Holger Kruse wrote:
> As mentioned before, this is because we are extremely busy. I'll > make an attempt at a response though. In any case, this should be > considered my personal opinion only. It does not represent the > official position of REBOL Technologies... >
I'm sure that all of us who have written and deployed code for a living can understand schedule pressure. Speaking for myself, THANK YOU VERY MUCH! for taking the time to respond. I really cannot emphasize enough how much I appreciate having someone from RT giving us the benefit of your perspective on these issues! (May I respectfully reserve the right to disagree, however? ;-) PREAMBLE TO PAIN
> First of all, I consider most of what Ladislav mentioned to be > the result of "switching pain", difficulties when switching > from one language to another one, and adjusting to a different > way of thinking... >
Having used and/or taught at least a couple of dozen languages in my career, I'll be the first to acknowledge that the phenomenon you labeled "switching pain" is a real one. I've certainly seen my share of c programs written in c++, COBOL programs written in c, FORTRAN programs written in LISP, etc... However, I think it a bit hasty to dismiss the issues discussed in this thread as being primarily due to "switching pain". For my part, if that's the way it appears, then I have failed to make myself clear. Let me try to address the "pain" issue in a way that will clarify my focus. (To do so, I must use examples. The obvious tightrope-walk danger in examples is that too simple ones make the issue appear trivial and too complex ones offer a wealth of distracting side issues. I'll try to walk the rope, but ask everyone to consciously attempt to look past the examples to the issues involved.) ON COD/E On the code-vs-data topic, I hope we can stipulate that this is not an issue for this discussion. I have been pedantic on this point with some collegues who are looking at REBOL for the first time, but try to do so only when I really think they are in imminent danger of drawing a bogus conclusion. I think that the contributors to this thread all understand that REBOL does not draw that distinction in the same way that most other languages do. We would, I believe, all concur with the statement in R/CUG (page 3-3) that, "How information is processed determines whether it is code or data." However, if pressed on the terminology, I'd point out that some REBOL expressions, such as "Hi!" or 2 * 3 + 7 may be repeatedly evaluated without consequence, while others a: 0 loop 5 [a: a + a] write %answer.txt a do, in fact, have consequences on the data within the interpreter and/or on the environment (file system, etc.) If backed into the corner on the terminology, I'd suggest labeling the latter kind of expressions as "Consequences On Data/Environment" expressions, with the acronym of CODE. Of course, I sometimes forget to hit the shift key (and REBOL is case-insensitive anyway) so I trust that we all can accept "code" as equivalent to "CODE". ;-) ON THREE KINDS OF "PAIN" A programmer feels pain when the behavior of some code differs from what was intended/expected. Of course, it is always possible to tell the programmer, "It's your fault; your expectation was faulty!" Instead of simply blaming the programmer, however, let's look a little deeper in the the causes (plural!) of the pain and possible prevention. The programmer's expectations may have been based on: A) Another programming language. B) Common concepts widely used in programming and computing science. C) Other features within the same language. In general, I believe that the further one goes down this list, the less we can simply label any unfulfilled expectations as "switching pain" or blame the programmer. The further we go (especially when we reach the third item!), the more we should look to the concepts and notation of the language, the implementation of the language, and (at the very least!) the documentation of the language as being either the cause of the pain or at least the way to prevent future pain. ON FRUSTRATION The reason that the programmer's expectations were frustrated may have been based on: 1) The programmer hasn't had sufficient experience in programming (including programmers who know only one language!) 2) The programmer hasn't had sufficient experience with the language at hand (despite general experience in programming). 3) The language does not have sufficient documentation to allow the programmer to learn the language -- without exhaustive trial-and- error experimentation. 4) The language has features whose complexity requires that the programmer keep up with more "moving parts" and exceptions to be able to use them safely. 5) The language has features that are "emergent properties" rather than "designed concepts" -- that is, they are consequences of the interactions of other features of the language which even the language designer(s) may not have intended or anticipated. 6) The implementation of the language may be faulty -- compared either to its published specifications or (absent such specs) the initial intentions of its designer(s). 7) The language "grew" rather than being "designed". Again, the further we go down this list, the more we have to shift our focus to the concepts, notation, specifications, implementation, and documentation of the language (and away from the programmer) as the causes and remedies of the frustration. Note especially on #6 that the absence of a published specification means that it is not possible, even in principle, to state definitively whether a given instance of questioned behavior is an implementation bug! AFTERWORD I'm collecting specific examples of concepts and notation that I find to be complicated, inconsistent, or confusing. (Donations welcome.) This note is long enough that I won't try to append the partial list at this point. However, I'm trying very hard to focus on things that (IMHO) belong to Expectations B and C, and to Frustrations 3 thru 7. As a single example of the kind of internal inconsistency I've mentioned, consider the relationship between the order of values and their use in retrieving data by position. We can compare integer! values:
>> 0 < 1 == true >> 0 <= 1 == true
and the results of converting integer! values to other numeric types
>> to-decimal 0 < to-decimal 1 == 1 >> to-decimal 0 <= to-decimal 1 == 1
When I use integer! values to select a value, the order of the positions of retrieved values is the same as the order of the selection values.
>> foo: ["a" "b" "c" "d"] == ["a" "b" "c" "d"] >> pick foo 1 == "a"
<<quoted lines omitted: 3>>
>> fum: 2 == 2 >> foo/:fum == "b"
Now, notice that we aren't allowed to compare logic! values????
>> to-logic 0 < to-logic 1
** Script Error: Expected one of: integer! - not: logic!. ** Where: to-logic 0 < to-logic 1
>> false < true
** Script Error: Cannot use lesser? on logic! value. ** Where: false < true However, we are allowed to convert between integer! and logic! (in either direction).
>> to-integer true == 1 >> to-integer false == 0 >> (to-integer false) < (to-integer true) == true >> (to-integer to-logic 0) < (to-integer to-logic 1) == true
This seems consistent with the forbidden comparison that would apparently have "false" less than "true". [ Note in passing that the frequently-used ordering convention of "false < true" has the benefit that the boolean (or "logical" if you prefer) operator "implies" -- as in "P implies Q" -- can be written as "p <= q". I freely admit that this is an Expectation B issue. ] On the other hand:
>> pick foo true == "a" >> pick foo false == "b" >> fum: true == true >> foo/:fum == "a" >> fum: false == false >> foo/:fum == "b"
So, we can't compare logic! values, apparently due only to some type-checking rule in the comparison operators. But if we could, the way they inter-convert with integer! values would lead us to conclude that FALSE precedes TRUE. But that is inconsistent with the fact that the TRUE-th position of a block precedes the FALSE-th position of the block! Except my passing reference to common convention, all of the above discussion has to do with completely REBOL-ish things: comparing REBOL values, converting between REBOL datatypes, and using REBOL values to pick values from a block. This is only a tiny, somewhat arbitrary, example; I hope it helps convey that the issue is not simply an inexperienced programmer complaining that REBOL is not Visual-PerlGOL-2000-Octothorp! -jn-

 [8/32] from: rebol:techscribe at: 27-Oct-2000 1:29


Hi Joel, let me correct a little mistake in your examples Joel Neely wrote:
> Now, notice that we aren't allowed to compare logic! values???? > >> to-logic 0 < to-logic 1
<<quoted lines omitted: 3>>
> ** Script Error: Cannot use lesser? on logic! value. > ** Where: false < true
The error you report has to do with precedence. REBOL is attempting to compare the integer 0 to the logic value true (resulting from to-logic 1). Compare to the parentheses you use further down, when comparing to-integer false to to-integer true. The parentheses are needed here as well.
> >> (to-integer false) < (to-integer true) == true >
The correct notation would be
>> (to-logic 0) < (to-logic 1)
which generates a different error, namely ** Script Error: Cannot use lesser? on logic! value. ** Where: (to-logic 0) < (to-logic 1) You can demonstrate the same behavior a little simpler by using
>> false < true
which generates the same error. If you look at the correct error message, I think you will agree with me that without a doubt this is a bug, since help lesser? reports that lesser? accepts any datatype. Since logic! is a datatype, lesser? should accept true and false as arguments. As a bug it does not really qualify as a design issue. Note that we can compare
>> true = false
== false and
>> true <> false
== true So comparisons are defined for logic values, and the comparison fails in this case, because it is not implemented correctly.
> On the other hand: > >> pick foo true == "a"
<<quoted lines omitted: 5>>
> So, we can't compare logic! values, apparently due only to some > type-checking rule in the comparison operators.
The same applies to this observation. It is not a type-checking rule, just plain old buggy implementation.
> But if we could, > the way they inter-convert with integer! values would lead us to > conclude that FALSE precedes TRUE. But that is inconsistent with > the fact that the TRUE-th position of a block precedes the FALSE-th > position of the block!
I don't agree with this observation. What makes you think that "TRUE-th position of a block precedes the FALSE-th position of the block"? Hope this sheds a little light, Elan

 [9/32] from: rebol:techscribe at: 27-Oct-2000 1:53


Hi Holger, this is a very lucid and useful explanation. Thanks for taking the time. Holger Kruse wrote:

 [10/32] from: g:santilli:tiscalinet:it at: 27-Oct-2000 11:29


Joel Neely wrote:
> >> to-decimal 0 < to-decimal 1 == 1 > >> to-decimal 0 <= to-decimal 1 == 1
This should be:
>> (to-decimal 0) < (to-decimal 1) == true >> (to-decimal 0) <= (to-decimal 1) == true
or:
>> lesser? to-decimal 0 to-decimal 1 == true >> lesser-or-equal? to-decimal 0 to-decimal 1 == true > >> to-logic 0 < to-logic 1
Same problem here. Regards, Gabriele. -- Gabriele Santilli <[giesse--writeme--com]> - Amigan - REBOL programmer Amiga Group Italia sez. L'Aquila -- http://www.amyresource.it/AGI/

 [11/32] from: lmecir:geocities at: 27-Oct-2000 15:00


Hi all, I think, that I supplied a few useful examples concerning WYSIWYG Rebol programming. Now it's time for a definition: Definition#1: -------------- I call a Rebol value Self Modifying (Rebol Code), if the evaluation of it (using Do e.g.) causes its change. Definition#2: ------------- I call a Rebol Value WYSIWYG (Rebol Code), if the evaluation of it doesn't cause an evaluation of a Self Modifying Rebol Code. Regards Ladislav

 [12/32] from: joel:neely:fedex at: 27-Oct-2000 8:25


Hi, Elan, Nice to hear from you! I was beginning to worry for your health. (I've had three deaths and one wedding in my family this summer, and an emergency hospitalization myself, so my own attendance has been spotty at times...) [elan--loop--com] wrote:
> > Now, notice that we aren't allowed to compare logic! values???? > >
<<quoted lines omitted: 7>>
> attempting to compare the integer 0 to the logic value true > (resulting from to-logic 1).
Yes, the *first* error above is due to precedence. I made a mistake doing cut-and-paste from the REBOL console and grabbed the wrong lines.
> You can demonstrate the same behavior a little simpler by using > >> false < true > which generates the same error. >
I did just that, in the *second* error message quoted above.
> If you look at the correct error message, I think you will agree > with me that without a doubt this is a bug, since help lesser? > reports that lesser? accepts any datatype. >
I'll agree that the HELP description and the actual behavior are out of synch. Whether we call that a bug in the implementation or an error in documentation depends on having an authoritative spec.
> Since logic! is a datatype, lesser? should accept true and false > as arguments. As a bug it does not really qualify as a design > issue.
Without knowing the original intent (i.e., having a published spec) I cannot say with confidence whether the defect is in the HELP text or in the behavior. Treating the entire product (REBOL) as a black box, I can say with confidence that there *is* a defect. However, this was only a minor side point. My main complaint was the ordering issue, about which more follows.
> Note that we can compare > >> true = false
<<quoted lines omitted: 4>>
> So comparisons are defined for logic values, and the comparison > fails in this case, because it is not implemented correctly.
No! You're mixing apples and fruit here! There is an important qualitative difference between = and <> on the one hand, and < , <= , > , and >= on the other. The first two operators check *identity* of their operands, while the last four check the *order* of their operands. There are *many* cases for which an identity test is highly useful but for which no natural, intuitive, or universally meaningful order relation applies. For instance, I can ask whether two functions are equal or differ, and it should be clear what that means. OTOH, what sense does it make to ask whether one function is "less than" another? Of course, we can invent all sorts of possible orderings for various purposes, but I hope it is clear that evaluating
>> 4 < 6.7
== true is inherently more universal and meaningful than evaluating
>> :print < :prin
** Script Error: Cannot use lesser? on native! value. ** Where: :print < :prin We can certainly make up some arbitrary conventions to give that expression meaning: * lexical order of the code as binary strings, * length of code, * date/time on which most recently defined, * a partial order based on the convention that :f1 < :f2 if f1 contains a call to f2 but I hope its clear that those are all ad hoc and not nearly of the same nature as a simple equality test.
> > On the other hand: > >
<<quoted lines omitted: 9>>
> The same applies to this observation. It is not a type-checking rule, > just plain old buggy implementation.
Try as I may (and I have), I cannot find any basis for this statement. If you can give me one, I'd be most grateful. Perhaps we are using the word "bug" differently. To me it has only one meaning: there is a bug exactly (and only) when code fails to conform to the governing specification. If there's no such specification, I am not entitled to label as a bug any piece of behavior I find unpleasant. I can only point out inconsistencies with the rest of the language as *potential evidence* of a defect in design, implementation, documentation, or planning. That was the whole purpose of the example.
> I don't agree with this observation. What makes you think that > "TRUE-th position of a block precedes the FALSE-th position of > the block"? >
I apologize if my example wasn't sufficiently clear, but I really don't know how to make it much clearer.
>> foo: ["a" "b" "c" "d"] == ["a" "b" "c" "d"] >> pick foo true == "a" >> pick foo false == "b"
Is it not clear that the position of "a" precedes the position of b in Foo? Is it not clear that TRUE and FALSE were used to obtain those values? My "-th" verbal abbreviation was an analogy based on using
>> pick foo 1.5 == "a" >> pick foo 2.3 == "b"
as the basis of saying that the 1.5-th position of Foo precedes the 2.3-th position of Foo. -jn-

 [13/32] from: joel:neely:fedex at: 27-Oct-2000 9:03


Hi, Gabriele, Thanks for catching the errors! With the typing corrections made, the same point about the ordering of values still applies. Perhaps my point would have been better made by saying the following:
>> foo: ["a" "b" "c" "d"] == ["a" "b" "c" "d"]
Therefore Foo contains a sorted set of values, which means that if the positions are in order, the values are in order.
>> 1 < 2 == true >> (pick foo 1) < (pick foo 2) == true
This is true even when the "positions" are not integers, and seems (at first!) consistent with converting the non-integer "positions" to integer! values.
>> 1.5 < 2.3 == true >> (to-integer 1.5) < (to-integer 2.3) == true >> (pick foo 1.5) < (pick foo 2.3) == true >> (pick foo to-integer 1.5) < (pick foo to-integer 2.3) == true
We're not allowed to Pick using a character! value directly, but can still get results that are consistent with the explicit order of character! values.
>> #"^a" < #"^b" == true >> (to-integer #"^a") < (to-integer #"^b") == true >> pick foo to-integer #"^a" == "a" >> pick foo to-integer #"^b" == "b" >> (pick foo to-integer #"^a") < (pick foo to-integer #"^b")
== true We're not allowed to compare logic! values directly, but there is an implicit order based on converting between logic! and integer!
>> to-integer false == 0 >> to-integer true == 1 >> (to-integer false) < (to-integer true) == true
However, using logic! values with Pick breaks the pattern of "ordered positions contain ordered values for a sorted block"
>> (to-integer false) < (to-integer true) == true >> (pick foo false) < (pick foo true) == false !!!!!!!!!
Conclusion: when using most datatypes to pick values from a sorted block (within the length of the block), it is true that (to-integer pos-1) < (to-integer pos-2) will guarantee that (pick s-blk pos-1) < (pick s-blk pos-2) when we are allowed to pick directly, and will guarantee (pick s-blk to-integer pos-1) < (pick s-blk to-integer pos-2) when we must make the conversion explicit. This is NOT the case with logic! values, hence my observation that this is an example of where one must simply memorize an exception to a general rule. It is certainly possible to respond by saying "Well, memorize the exceptions then!" The more exceptions one must memorize, the greater the difficulty of using the language and the greater the likelihood of error. -jn-

 [14/32] from: jeff:rebol at: 27-Oct-2000 9:09


Howdy, Joel:
> I really cannot emphasize enough how much I > appreciate having someone from RT giving us the benefit of > your perspective on these issues! > > (May I respectfully reserve the right to disagree, however? > ;-)
Yeah, but when we respond then you guys don't let us off the hook and you hold us to the fire!! :-) Hah hah -- Just jokin around, ya-know!
> ON FRUSTRATION > > 3) The language does not have sufficient documentation to > allow the programmer to learn the language -- without > exhaustive trial-and- error experimentation.
We've lost Carl for weeks on end while he did a rewrite of our docs for reason 3 above.
> 4) The language has features whose complexity requires that > the programmer keep up with more "moving parts" and > exceptions to be able to use them safely.
Few things in REBOL can be generally classed number 4 above -- or at least fewer things than many (most) other languages out there. Simple things are simple to do, and you can do hard things too (but hard things can still be hard!).
> 5) The language has features that are "emergent properties" > rather than "designed concepts" -- that is, they are > consequences of the interactions of other features of the > language which even the language designer(s) may not have > intended or anticipated.
The above is by design, just like strategy is an emergent property of chess which is based on simple movement rules of the pieces. REBOL is full of these things, where you suddenly realize a new use for an old function. You suddenly realise that instead of: either found? val1 [foo: val1][ either found? val2 [foo: val2][ either found? val3 [foo: val3][ foo: default-val ] ] ] you can just do this: foo: any [val1 val2 val3 default-val] All this time you might have thought ANY was just this lazy conditional evaluator, to be used only after IF or EITHER, but really it makes for a very useful value selector by placing values in it in the order of precedence. This arises from a simple rule in the design of REBOL that all functions that can return a value do. Here's another one of those type emergent language capabilities (a kind of silly one that I'm fond of): to-tuple loop 3 [append [] random 255] Above capitalizes on the nature of literal blocks and the fact that loop returns a value. Or: if loop 20 [do-something now/time > 12:00:00] [print #afternoon] These emergent properties constitute the expressiveness of REBOL. Fluency in REBOL is a matter of finding slicker ways to express things (I think ANY is much slicker than EITHER EITHER EITHER (-:).
> 6) The implementation of the language may be faulty -- > compared either to its published specifications or (absent > such specs) the initial intentions of its designer(s).
By any other name: BUG. Yes, software has bugs. Bugs are frustrating.
> 7) The language "grew" rather than being "designed".
The language's design grew. Carl has spent 15+ years designing REBOL, building different prototypes of REBOL. An amazing amount of attention to detail has come from this. The basic core of the language reflects this great design legacy, and is witnessed in the consistency of how datatypes work, the language's seamless polymorphism, and the general benevolence of the language (by that I mean that REBOL tries to help you wherever it can-- it's like there's always a coat hook placed exactly where you want to hang your coat). The areas where REBOL has "grown" is in the "features" layer. Features are sometimes added which don't support the full gamut of operations that most other datatypes have, or they have some exception to a rule. Features occasionally break the consistency that the design of REBOL has sought (in my opinion very successfully) to achieve otherwise.
> Again, the further we go down this list, the more we have > to shift our focus to the concepts, notation,
<<quoted lines omitted: 5>>
> whether a given instance of questioned behavior is an > implementation bug!
We all want to understand what hurdles people face in learning and adopting REBOL. We rap about this often in the office, and your breakdown above is pretty good, though I would throw out 7, 5 and 4 and just look at the remaining issues: Bugs and docs. To those two I'd add support. A user entering into a new language like REBOL wants to know they can find a thriving community of users that can pass along guru tips and quickly diagnose and help them through newbie problems. They want tutorials, and FAQs and how-tos, and cool examples, etc. REBOL Tech.'s worked hard on providing a lot of that stuff, and many others have as well, but this is one big area where any one can contribute in a meaningful way to the spread and adoption of REBOL. (As Dan recently mentioned, we're also working on a contribution system where people can contribute to the language more directly. Basically this will entail the generous programmer giving us the copyrights to their contributed source code so, that if we decide it's appropriate and of a quality to include, we can place it in the core, or include it in the distribution. We'll also add a contributors page to our web site with a description of what someone has provided. We hope this will be a great way for people to add to the language for the benefit of all. More details to come soon!!)
> and the results of converting integer! values to other > numeric types > > >> to-decimal 0 < to-decimal 1 == 1 > >> to-decimal 0 <= to-decimal 1 == 1
Uh, I think you want:
>> (to-decimal 0) < to-decimal 1 == true >> (to-decimal 0) <= to-decimal 1 == true
Think of the infix < and <= operators as shifting around prior to evaluation to become prefix operators. The first case would then actually be interpreted as: to-decimal (< 0 to-decimal 1) to-decimal (<= 0 to-decimal 1) Here is a case of a subtlety about infix operators that may occasionally bite people (one for the docs). The general rule I use is "compound expressions on the left side of an infix operator must be parenthesized". Or, "always put compound expressions to the right of an infix operator", eg: 0 = to-integer "0" == true ;- correct to-integer "0" = 0 == 0 ;- not what you want The second case yields: to-integer (= "0" 0)
> Now, notice that we aren't allowed to compare logic! > values???? > > >> to-logic 0 < to-logic 1 > ** Script Error: Expected one of: integer! - not: > ** logic!. Where: to-logic 0 < to-logic 1
The error above is complaining because that expression is seen as: to-logic (< 0 to-logic 1) But in either case...
> >> false < true > ** Script Error: Cannot use lesser? on logic! value. > ** Where: false < true
[...]
> On the other hand: > >> pick foo true == "a" pick foo false == "b" fum: true
<<quoted lines omitted: 7>>
> position of a block precedes the FALSE-th position of the > block!
A key point here is that REBOL is context dependent. Because TRUE means something in the context of PICK, it does not necessarily imply an ordinal ordering of logic values. People usually think "TRUE" "FALSE", in that order. EITHER places the TRUE block first, the ELSE/FALSE block second. Pick thinks of TRUE/FALSE in that way. Now when someone is converting a logic value to a number they usually have a different expectation: TRUE being 1, and FALSE being 0. That would be my normal expectation, but now we have two clashing expectations if we consider them together (as you have done here). Should we change it so that we have: EITHER condition [FALSE-BLOCK] [TRUE-BLOCK] to accomplish some consistency with logic datatypes? NO WAY! The best you can do with things like this is to have the language be context dependent. RETURN means something totally different in a layout than it does in a function body. The great aspiration for the language once it has booted to the prompt is that things mean what they mean where ever they mean it in the most intuitive way (--now say that 3x fast! (:). Whenever we run into situations where we say "gee, that really dosn't work the way people would 'normally' expect" we fix them. In the next releases there are some fixes like that, for instance to-binary of a tuple and inserts and joins on binary values have recently been fixed to work as people more generally expect.
> Except my passing reference to common convention, all of > the above discussion has to do with completely REBOL-ish
<<quoted lines omitted: 5>>
> programmer complaining that REBOL is not > Visual-PerlGOL-2000-Octothorp!
You make many valid points, Joel, but let's boil it down to the three basic issues: fixing bugs, adding docs, and (community) support. Switching pain is exacerbated by lack of those three things. Adoption of REBOL is hindered by switching pain. REBOL Tech should work on the first two and try to facilitate the third as much as possible. The thing I just want to exclude from the equation is the design of REBOL. REBOL's design is solid and remarkably consistent, but most importantly it is context dependent. The odd cases where we do get bitten by apparent inconsistencies are far less than the ongoing experiences people have with REBOL where things DO work the way people expect them to. When something does not work in the way the average user would expect, let us know (you all are very good about that!) and we'll throw those in the bug pile and fix em! :-) Saludos-- -jeff

 [15/32] from: rebol:techscribe at: 27-Oct-2000 12:39


Joel Neely wrote:
> Hi, Elan, > > Nice to hear from you! I was beginning to worry for your health. > (I've had three deaths and one wedding in my family this summer, > and an emergency hospitalization myself, so my own attendance has > been spotty at times...)
Sorry to hear of your losses. Hope everything is well with you. I have simply been (and continue to be) very busy, trying to catch up with the time I lost writing the book (time measured in terms of income that is). I will try to take some time over the weekend to respond to your message. Can't do it right now, because I have to run (hate mettings ....). Take Care, Elan

 [16/32] from: rebol:techscribe at: 27-Oct-2000 16:03


Hi Jeff, [jeff--rebol--net] wrote:
> You suddenly realise that instead of: > either found? val1 [foo: val1][
<<quoted lines omitted: 6>>
> you can just do this: > foo: any [val1 val2 val3 default-val]
Deja vu. Last night (early this morning) I came up with this exact idea, while writing a parse rule that had to distinguish three cases. Take Care, Elan

 [17/32] from: rishi:picostar at: 27-Oct-2000 15:34


>>>>
... The great aspiration for the language once it has booted to the prompt is that things mean what they mean where ever they mean it in the most intuitive way ... <<< I think this is a key sentence. After trying to find a general theory on what datatypes are manipulated by reference and what datatypes are manipulated by values, it just hit on me that the general theory of references in Rebol is simply what makes most sense for that particular datatype. You don't say: All series are treated as reference and all other datatypes are treated as values (not true btw) instead you simply look at the datatype you are using and say: hmm...objects can get quite big so it makes sense to treat it as a reference by default The more I program in rebol, the more I realize that all of its' features are simply optimized for the most common case that you'd expect. Kind of like how the human brain optimizes itself for most common, not most general, tasks. Rishi Previously, you ([jeff--rebol--net]) wrote:

 [18/32] from: joel:neely:fedex at: 27-Oct-2000 23:06


Hi, Jeff, Thanks for chipping in! Most of my musings and comments arise from a well-meaning attempt to find a balance between Sir Arthur Eddington There are two kinds of science, Physics and stamp collecting. and Ralph Waldo Emerson A foolish consistency is the hobgoblin of little minds... Of course we can't omit Lily Tomlin Man invented language to satisfy his deep need to complain. from our list of philosophers! ;-) I certainly hope that my remarks are coming across as (part of) my attempt to help give "aid and comfort" to REBOL. That's the spirit in which I intend them. My lists were not meant as value judgements of any kind (much less, of a negative kind), but as a suggestion of factors that affect every language, every programmer, and every question, all in varying degrees. I've been on the list for about a year now, and occasionally see questions or comments go by followed by a response that tends toward (I'm exaggerating for effect here!), "Well, you just don't 'get' REBOL yet. You're still thinking in some other language." As I tried to say in my earlier post, a change of mind-set is often worth including in the "pain pill", but I think it is also sometimes too easy to assume that the entire problem is ONLY due to "switching pain". (I'm not picking on Holger here; he just happened to use a memorable phrase that caught my fancy!) By listing a large number of possible factors, I'm trying to encourage a multimodal approach to pain therapy. I freely confess that as a sometime teacher, I'm constantly searching for better, simpler, clearer ways to explain the concepts of software design and development, and the tools of that discipline -- including programming languages. I'm also thoroughly persuaded by Dijkstra's statements that * complexity is the chief enemy of software developers, and * elegance is not a luxury, but a necessity. [jeff--rebol--net] wrote:
> Yeah, but when we respond then you guys don't let us off the > hook and you hold us to the fire!! :-) Hah hah -- Just jokin > around, ya-know! >
Well, it's more fun that pulling legs off of ants! ;-) OBTW, please note that I kept saying "the language" in an attempt at a constant reminder that I wasn't criticizing REBOL. I was offering a list that I think can apply to almost ANY language.
> > 3) The language does not have sufficient documentation to > > allow the programmer to learn the language -- without > > exhaustive trial-and- error experimentation. > > We've lost Carl for weeks on end while he did a rewrite of > our docs for reason 3 above. >
And I, for one, am VERY grateful. It's a big step forward. I also am working up a list of errata and suggestions for improvement (but in background mode, due to schedule pressures). I'll send them as soon as I can. However, one significant area I can think of (at least the current pebble in my shoe ;-) is the need for documentation on contexts. Being a Bear of Small Brain, it's taken me quite a while to get my head around a few facts on that subject (and with quite a bit of help from the list, I must say!). However, I still can't find anywhere in the available documentation where I can go to confirm or refute what I think I've learned on that subject.
> > 4) The language has features whose complexity requires that > > the programmer keep up with more "moving parts" and
<<quoted lines omitted: 3>>
> out there. Simple things are simple to do, and you can do > hard things too (but hard things can still be hard!).
A few examples, to make the point: a) We can convert integer! and decimal! values to time! values, with the (reasonable, IMHO) assumption that the units are seconds.
>> to-time 3600 == 1:00 >> to-time 360.5 == 0:06:00.5
However, we can't go the other way (which I would think of as equally reasonable).
>> to-integer to-time 3600
** Script Error: Invalid argument: 1:00. ** Where: to integer! :value
>> to-decimal to-time 360.25
** Script Error: Invalid argument: 0:06:00.25. ** Where: to decimal! :value b) Most loop control functions (e.g., Forskip, Foreach, For, Repeat) "localize" the controlled word(s). However, Forall does not do so, leaving the value of the controlled word altered upon loop exit. c) On the subject of "simple things are simple to do" I'd ask: Isn't changing a value in a data structure as simple a concept as retrieving it? Isn't changing (or retrieving) a value based on a calculated "place" in the structure almost as simple a concept as changing (or retrieving) a value at a fixed "place" (except for the effort of calculating that "place")? Given an array (nested blocks of equal size)...
>> a: array/initial [3 3 3] 0
== [[[0 0 0] [0 0 0] [0 0 0]] [[0 0 0] [0 0 0] [0 0 0]] [[0 0 0] [0 0 0] [0 0 0]]] ...it's easy to alter and fetch values at fixed positions...
>> a/1/1/1: 111 a/2/2/2: 222 a/3/3/3: 333 a/2/2/2
== 222
>> a
== [[[111 0 0] [0 0 0] [0 0 0]] [[0 0 0] [0 222 0] [0 0 0]] [[0 0 0] [0 0 0] [0 0 333]]] ...and not very hard to fetch values at easy-to-compute positions...
>> for i 1 3 1 [print a/:i/:i/:i]
111 222 333 ...but now write a simple expression to set those "diagonal" positions back to zero! (Without explicitly writing three set-path expressions, please... That's not a general solution!) It's not nearly so easy as for i 1 3 1 [a/:i/:i/:i: 0] ...because that's not legal code! One can certainly create such monstrousities as... for i 1 3 1 [ do reduce [ to-set-path reduce ['a i i i] 0 ] ] == [0 0 0]
>> a
== [[[0 0 0] [0 0 0] [0 0 0]] [[0 0 0] [0 0 0] [0 0 0]] [[0 0 0] [0 0 0] [0 0 0]]] ...but that's hardly keeping "simple things simple"! There are other variations on this same theme, such as incrementing all values in the array, incrementing an element selected by three values obtained from input, etc. but they all share the same theme of significantly-different effort to do things that are conceptually equally easy. d) On the subject of confusing inconsistencies vs. "foolish consistency" we can consider modifications to series values.
>> s-b: ["a" "b" "c" "d"] == ["a" "b" "c" "d"] >> s-l: make list! s-b == make list! ["a" "b" "c" "d"] >> s-h: make hash! s-b == make hash! ["a" "b" "c" "d"] >> s-s: "abcd" == "abcd"
When we Append to any of these, the side effect is to extend the series and the result is the extended series. Reasonable and consistent, IMHO.
>> append s-b "e" == ["a" "b" "c" "d" "e"] >> append s-l "e" == make list! ["a" "b" "c" "d" "e"] >> append s-h "e" == make hash! ["a" "b" "c" "d" "e"] >> append s-s "e" == "abcde"
When we Change any of these, the side effect is to alter a position in the series, but the result is equivalent to Next on the altered series. Consistent between series types, but inconsistent with the result of Append. I think this is a perfectly reasonable inconsistency, as it allows a sequence of "chained" applications of Change to affect consecutive positions, rather than repeatedly altering the same position (which probably wouldn't make sense!) In other words, there is a "higher consistency", in that the result of each operation caters to successive applications of the same operation in a natural way.
>> change s-b "x" == ["b" "c" "d" "e"] >> change s-l "x" == make list! ["b" "c" "d" "e"] >> change s-h "x" == make hash! ["b" "c" "d" "e"] >> change s-s "x" == "bcde"
However, the reference to the series does not have its position altered; again this seems perfectly reasonable.
>> s-b == ["x" "b" "c" "d" "e"] >> s-l == make list! ["x" "b" "c" "d" "e"] >> s-h == make hash! ["x" "b" "c" "d" "e"] >> s-s == "xbcde"
When we Insert a value, the side effect is to modify the series, and the result is positioned immediatly following the insertion. Again, this follows our "higher consistency" idea of supporting consecutive Insertions in left-to-right order.
>> insert s-b "z" == ["x" "b" "c" "d" "e"] >> insert s-l "z" == make list! ["x" "b" "c" "d" "e"] >> insert s-h "z" == make hash! ["x" "b" "c" "d" "e"] >> insert s-s "z" == "xbcde"
BUT, this time there is a datatype-dependent inconsistency! Most of the series references have unaltered positions, but for the list! case, the position is different.
>> s-b == ["z" "x" "b" "c" "d" "e"] >> s-l == make list! ["x" "b" "c" "d" "e"] >> s-h == make hash! ["z" "x" "b" "c" "d" "e"] >> s-s == "zxbcde"
This means that I have an inconsistent side-effect, with no offsetting benefit that I can think of. If I want to write a generic series-handling function which uses Insert, I may have to make a special-case test for whether the argument series is a list! and include
>> s-l: head s-l == make list! ["z" "x" "b" "c" "d" "e"]
code to re-establish a consistent state of affairs. The same issue recurrs (with a different side-effect on list! values) with Remove. Inconsistency is not always unreasonable, but there should be a good reason! e) The RCUG specifically states that "the concept of none is not the same as an empty block, empty string, or null character". Yet there's an inconsistency in Parse when an empty string (zero IS a valid string length) matches a pattern. split-string: function [ s [string!] d [string!] ][ blk seg delim data ][ data: complement delim: charset d blk: copy [] parse/all s [ copy seg any data (append blk seg) any [ delim copy seg any data (append blk seg) ] ] blk ]
>> split-string "this~is~a~test" "~"
== ["this" "is" "a" "test"]
>> split-string "this~is~a~~test~with~~empty~segments" "~"
== ["this" "is" "a" none "test" "with" none "empty" "segments"] Parse evaluated the zero-length segments in the last case as being NONE instead of being "". The cost of this inconsistency is that one must either complicate the function above into split-string: function [ s [string!] d [string!] ][ blk seg delim data ][ data: complement delim: charset d blk: copy [] parse/all s [ copy seg any data (append blk any [seg ""]) any [ delim copy seg any data (append blk any [seg ""]) ] ] blk ]
>> split-string "this~is~a~~test~with~~empty~segments" "~"
== ["this" "is" "a" "" "test" "with" "" "empty" "segments"] or remember to post-process the result to replace NONE with empty strings, or (shudder!) write all down-stream code to accept NONE as equivalent to an empty string. This has the downstream cost of biting anyone who uses Parse-XML results, expecting to be able to use Select to fetch attribute values, interpreting a result of NONE as indicating that the attribute did not appear.
>> foo: parse-xml {<a b="1" c="">Hi!</a>}
== [document none [["a" ["b" "1" "c" none] ["Hi!"]]]] At some point in code that traverses the nested block structure we'd arrive at the equivalent of...
>> attribs: second first third foo
== ["b" "1" "c" none] ...and then say something like...
>> select attribs "c"
== none ...which will require additional code to distinguish from...
>> select attribs "x"
== none ...perhaps something like... if found? find attribs "c" [any [select attribs "c" ""]] == "" if found? find attribs "b" [any [select attribs "b" ""]] == "1" if found? find attribs "x" [any [select attribs "x" ""]] == none We could have avoided all of this if Parse treated empty strings as empty strings. There is certainly some interaction between this point and point #6, but I'll deal with that question at that "point". ;-)
> > 5) The language has features that are "emergent properties" > > rather than "designed concepts" -- that is, they are > > consequences of the interactions of other features of the > > language which even the language designer(s) may not have > > intended or anticipated. >
[snip]
> ...REBOL is full of these things, where you suddenly realize a > new use for an old function. >
That's not at all what I meant by an emergent property. It's not an issue of whether I as a programmer think of an interesting way to use the value of an expression, whether it's a trick that works in other languages
> foo: any [val1 val2 val3 default-val]
or one that's more specific to REBOL features
> to-tuple loop 3 [append [] random 255] > if loop 20 [do-something now/time > 12:00:00] [print #afternoon]
Rather, its a question of whether the language's concepts or implementation interact in a way that imposes limits on how I can do things or causes subtle, context-dependent changes in behavior that may be difficult to anticipate. For example, Use provides a way to provide a small "scope" for temporarily-needed words without worrying about name collisions with the surrounding context. (This is A Good Idea, as REBOL has situations in which the clearest way to write code is to set a variable to the value of an expression, and then use the variable, rather than using the expression directly.) However, Use is apparently implemented via definitional scope, rather than dynamic scope or lexical scope. This means that utilizing Use inside a recursive function may have exactly the opposite effect from its intended purpose. f: func [a /local b] [ either a = 0 [ exit ][ use [c] [ c: a b: a print ["in " c b] f (a - 1) print ["out" c b] ] ] exit ]
>> f 3
in 3 3 in 2 2 in 1 1 out 1 1 out 1 2 out 1 3 Here, instead of shielding the inner block from the outer world, Use actually exposes that block to interference from elsewhere. Surely I'm not to believe that we have a mechanism that was designed specifically to be unusable with recursive functions? Isn't it reasonable of me to assume that this is an unintended emergent consequence of the separate choices to allow functions to be recursive and to implement Use via definitional scoping? The most straightforward way I know of to get the desired degree of "insulation" is to write f: func [a /local b] [ either a = 0 [ exit ][ do func [/local c] [ c: a b: a print ["in " c b] f (a - 1) print ["out" c b] ] ] exit ]
>> f 3
in 3 3 in 2 2 in 1 1 out 1 1 out 2 2 out 3 3 and that't not a terrible cost (for an old Lisp hacker, at least), but I suspect it's not terribly obvious to the typical beginning REBOL programmer. One more example, and I'll close this point. Does is nice syntactical sugar for a no-argument, no-locals function that packs up some behavior in a single word. Another nice feature of REBOL is the fact that words "controlled" by (most!) looping functions are localized, so that after the loop exit the prior value of the word has not been destroy

 [19/32] from: g:santilli:tiscalinet:it at: 28-Oct-2000 15:26


Hello Joel! On 27-Ott-00, you wrote: JN> With the typing corrections made, the same point about the JN> ordering of values still applies. Yup --- I didn't say that explicitly, but I agree with you here. Perhaps there was a reason for mapping TRUE to the first position, but it might be a bug as well (even if it seems deliberate to me, because if they were just doing a conversion everything would be ok). Simpler means with less exceptions, Gabriele. -- Gabriele Santilli <[giesse--writeme--com]> - Amigan - REBOL programmer Amiga Group Italia sez. L'Aquila -- http://www.amyresource.it/AGI/

 [20/32] from: brett:codeconscious at: 29-Oct-2000 3:29


Hi Joel,
> That sounds good. However, natural language and the common-sense > expectations of 'normal' people (or even programmers when they're > not programming) are notorious for their ambiguity and fuzziness.
Important qualities that traditional computing cannot deal with.
> I humbly propose that the need for precision in programming means > that rigorous, elegant, and minimalist definition should take > priority over everyday conversational comfort, should they ever > come into conflict.
Respectfully, I think you are putting up a language design goal that many (most?) computer languages have been aiming for for years. The same thing that many (most?) serious (experienced?) programmers would aim for in their programs. A mindset legacy of structured programming, provable programs, etc. There is nothing wrong with this - indeed it is necessary line of thinking, and up until recently I would have seconded your proposal for Rebol to follow this goal. Now I think otherwise. 1) If feel that "rigorous", "elegant" and "minimalist" are from the Mathematical space of thinking. Paradoxically, I think these concepts are very subjective. 2) I believe one of the main aims of Rebol is to be a useful language. Its practical and pragmatic approach has been the selling factor for me to invest my time in it.That said, I do agree with you (and others) that it needs a more detailed supporting documentation set on the language, but I can accept that the language itself is still stabilising. 3) "precision in programming" reminded me of the often heard "Arrgghh! It did what I said, not what I meant!". I'm hoping that Rebol will succeed in reducing the occurrence of this phrase via dialecting. I hope you don't find my comments frustrating - I am deliberately "coming in from a different angle". They are meant to invite the questioning of perhaps long held "truths". In the Rebel fashion ;) One last thing which has perplexed me with this whole thread. Your example of the ordering of logical values. Why? What do you use such a thing for? I've been trying to think back over my programming experience for a practical example of this but I've failed miserably. Maybe my experience wasn't broad enough or maybe I just needed a coffee! For me, I have to agree with Rebol, ordering logical values seems to be a nonsense. Grouping fine, but ordering?! Thus, pick's behaviour (using true/false as selector) as long as it is consistent with poke seems ok. Brett.

 [21/32] from: lmecir:geocities at: 28-Oct-2000 23:12


Hi, Joel wrote: ...
> e) The RCUG specifically states that "the concept of none is not > the same as an empty block, empty string, or null character". > Yet there's an inconsistency
This feature disturbed me too. ...
> Does is nice syntactical sugar for a no-argument, no-locals function > that packs up some behavior in a single word.
<<quoted lines omitted: 5>>
> The consequence of these two facts is that I cannot use a previously- > defined Does block which has access to the loop-controlled variable(s).
If am supporting the Rebol behaviour here, because I don't like such surprising changes in function behaviour depending on the place of its evaluation. If you would like to have a changed function, you can create a new one like: whatever: "nonsense" f: does [print whatever] for whatever 1 2 1 [do does bind second :f 'whatever] Regards Ladislav

 [22/32] from: joel:neely:fedex at: 28-Oct-2000 17:52


[rebol-bounce--rebol--com] wrote:
> > Another nice feature of REBOL is the fact that words "controlled" by > > (most!) looping functions are localized, so that after the loop exit
<<quoted lines omitted: 12>>
> f: does [print whatever] > for whatever 1 2 1 [do does bind second :f 'whatever]
I should have known that if I said "cannot" that some guru would come along and show a way! Perhaps I should change my sentence to read "cannot easily (without being Ladislav) use ..." ? ;-) Seriously, thanks for the correction! -jn-

 [23/32] from: jeff:rebol at: 29-Oct-2000 21:03


Howdy, Joel: [ . . . much snipping and it's still like 8 pages . . .]
> I've been on the list for about a year now, and > occasionally see questions or comments go by followed by a > response that tends toward (I'm exaggerating for effect > here!), "Well, you just don't 'get' REBOL yet. You're > still thinking in some other language."
It's a response that works for many people, and points to something that I, too, feel indicates a real phenomenon. I don't see what it buys to dismiss the phenomenon, really.
> As I tried to say in my earlier post, a change of mind-set > is often worth including in the "pain pill", but I think it > is also sometimes too easy to assume that the entire > problem is ONLY due to "switching pain".
Sure. That is too simple. There are many factors when it comes to developing living breathing languages, and to me it really comes down to the abilities of a language to solve problems and the cultivation of a thriving developer community. Today, languages with far less cleanliness of design and internal consistency as REBOL are vastly more popular by many orders of magnitude.
> > ...you guys don't let us > > off the hook and you hold us to the fire!! :-) Hah hah > > -- Just jokin around, ya-know! > > > > Well, it's more fun that pulling legs off of ants! ;-)
Seeing this just huge monster of an email you replied to me made my jaw drop! :) I simply can not keep up with this kind of letter writing! I have to write code, dear sir! Not trying to cop out, but please understand I can't do these marathon email sessions! :) [Though, I'll give it half a shot here..]
> I also am working up a list of errata and suggestions for > improvement (but in background mode, due to schedule > pressures). I'll send them as soon as I can.
Great! We always appreciate this kind of stuff pointing to aspects of non-orthogonality, even if we don't address it right away. Our priority lately has been express. We don't forget the improvements people offer, like Robert Muench compiled a list of improvements that could be made to parse (and many others out there too). This is a primary drive to getting our contribution system running.
> However, one significant area I can think of (at least the > current pebble in my shoe ;-) is the need for documentation > on contexts. ... I still can't find anywhere in the > available documentation where I can go to confirm or refute > what I think I've learned on that subject.
Well, the definition of contexts is context dependent. :) I have an operative definition of contexts that works for me: "where a word is looked up" [simple things should be simple to do... and simple to explain, too.]
> A few examples, to make the point: > > a) We can convert integer! and decimal! values to time! > values, > > with the (reasonable, IMHO) assumption that the units > are seconds. However, we can't go the other way (which > I would think of as equally reasonable).
Well, it is more than reasonable. You're apparently not running core 2.4.38. ;-)
> b) Most loop control functions (e.g., Forskip, Foreach, > For, > Repeat) "localize" the controlled word(s). However, > Forall does not do so, leaving the value of the > controlled word altered upon loop exit.
Actually forskip has the same behavior as forall, leaving the series pegged at the tail on completion (or left where ever you were when you did a break). This is by design. Forall and forskip are made to operate on series indexes. This allows you to modify series as you go, or to use variable amounts of look ahead, or perform other series operations on the series as you move it. The series is moved forward by moving the word that points to the series forward. x: [1 2 3 4 5 6] forskip x 2 [change x pick x random length? x] head x == [3 2 6 4 6 6] You can't do what's above with foreach. Foreach merely gives you consecutive values or sets of values from a series. Foreach is very handy when you are just pulling out data, but not modifying the series. So you have two cases and one for each. One common characteristic of REBOL design is you will find that a lot of functionality is accomplished by spliting the cases in two and providing a function for each. For, repeat, and loop aren't series iterators, so they don't really pertain here, except in that they use a local variables like foreach. Some people find foreach pegging the series to be disconcerting, but it is a rather core aspect of REBOL, and it has a valid reason why it works that way.
> c) On the subject of "simple things are simple to do" I'd > ask:
<<quoted lines omitted: 7>>
> for i 1 3 1 [a/:i/:i/:i: 0] > ...because that's not legal code!
or repeat i 3 [change at a/:i/:i i 0] It would be handier the way you have it, but I wouldn't go so far as calling the above "hard".
> d) On the subject of confusing inconsistencies vs. "foolish > consistency" we can consider modifications to series > values.
[. . .]
> >> insert s-b "z" == ["x" "b" "c" "d" "e"] > >> insert s-l "z" == make list! ["x" "b" "c" "d" "e"]
<<quoted lines omitted: 4>>
> unaltered positions, but for the list! case, the > position is different.
Yep. Lists are the black sheep of the series. They have absolute indexing instead of relative indexing. A list IS where it is. The reasons have to to do with what lists are, namely doubly linked lists, and how they are implemented in REBOL. It's a question of docs.
> e) The RCUG specifically states that "the concept of none is > not the same as an empty block, empty string, or null > character". Yet there's an inconsistency in Parse when an > empty string (zero IS a valid string length) matches a > pattern.
The problem of information loss you point out with none in parse more points to a bug, but it doesn't mean NONE is equivalent to to an empty string. There's a few more things Carl wants to do to PARSE before it will enter parse nirvana. [ . . . ]
> f: func [a /local b] [ > either a = 0 [
<<quoted lines omitted: 11>>
> world, Use actually exposes that block to interference from > elsewhere.
USE is actually primarily offered as a shield for the outside environment from whatever bindings you make inside the USE context than the other way around. I don't think the documentation ever portrays USE in the manner you are expecting. Also, what you are pointing at here is more a symptom of function contexts and how they work. In either case, indefinite extent is another issue we rap a fair bit about at the office, and one that we intend to poke at pretty soon. . . .
> The consequence of these two facts is that I cannot use a > previously- defined Does block which has access to the
<<quoted lines omitted: 14>>
> a function with a parameter (so that I can pass the loop > control value in to it).
FOR is implemented by creating a function. When you create a function you create a separate context where the loop variables live. TRY: SOURCE FOR Maybe you would like to make another version of FOR using a save/restore scheme which will let other things in the same scope have access to the loop variables? I think dealing with recursion and nested FORs might be the tricky two there.
> > By any other name: BUG. Yes, software has bugs. Bugs > > are frustrating. > > Yes, but it's more frustrating to be uncertain about > whether something IS a bug! I've seen disagreement on this > list between experienced REBOL programmers over whether a > particular piece of behavior is a bug or not. ..
Pioneers often have to go forward with out a perfect Rand McNalley road atlas and triple-A. :)
> In the following, though, I must disagree a bit. > >
<<quoted lines omitted: 8>>
> might be done either to prevent or alleviate that > confusion.
I see where you're coming from. Just like to keep things in scale. Fixing bugs, developing more docs and cultivating community support are the top three things to my mind that would make an immediate difference. An honest question: How could we know if there are new users coming in and suddenly being turned off because they see that in REBOL they can't test if: false < true ?
> I'm also not persuaded that REBOL (nor any programming > language designed by mortals ;-) is so perfect in design and > concept that any issues must be considered as implementation > glitches, explanation breakdowns, or user head gaps.
Naw, but as I said, REBOL's design is solid and very consistent. That's not saying perfect, it's just to say that it is, in my view, an excellent design. I don't think we'd all be here if REBOL was riddled with design problems (not implying that's what you're implying!) Just trying to put things into perspective. . . .
> >> letter: "i" == "i" select gpa letter == none > >> gpa/:letter
<<quoted lines omitted: 5>>
> questions are worth asking, rather than ruling some > possibilities out of bounds.
Path notation is a shortcut. A shortcut takes a different path through the woods. In the woods it trips on a branch and has an error.
> > ... They want tutorials, and FAQs and how-tos, > > and cool examples, etc. REBOL Tech.'s worked hard on
<<quoted lines omitted: 4>>
> > > I hope these essays are serving such an end.
Certainly if people (newbies especially) manage to read through these huge ol' emails! :) I think there's something to the idea of trying to accomplish too many things in one single email. There should be an automatic send cut off switch when an email has covered, say, 5 concepts. :)
> > > > EITHER places the TRUE block first, the ELSE/FALSE
<<quoted lines omitted: 5>>
> free to use a "linguistic" model instead of a numerical > order model for deciding how to arrange its parts.
Okay, so you would like the following: pick [#1st #2nd] false == #1st pick [#1st #2nd] true == #2nd and true > false == true so.. none < false == ? Also in REBOL, TRUE is equivalent in a test to: (not none and not false) so what are the results to the following: false < "foo" false < http://foo.bar false < 0 false < -1 false < print "This will be unset" etc... ?
> Actually, the underlying problem goes deeper. If we > allowed numeric labeling of elements in a collection to > follow the natural number progression of 0, 1, 2, ..., then > it would be even more obvious that FALSE selects the same > labeled position as 0 and TRUE selects the same labeled > position as 1.
which would be: pick [1 2] 0 == none ; for false pick [1 2] 1 == 1 ; for true Which means ....
> Before anyone responds with a claim that it's confusing for > the "first" position to be 0,
You would also like REBOL to be 0 based indexing?
> > Whenever we run into situations where we say "gee, that > > really dosn't work the way people would 'normally'
<<quoted lines omitted: 4>>
> programmers when they're not programming) are notorious for > their ambiguity and fuzziness.
And formalisms are notorious for their lack of conceptual accessibility.
> I humbly propose that the need for precision in programming > means that rigorous, elegant, and minimalist definition > should take priority over everyday conversational comfort, > should they ever come into conflict.
Good science should be easily explainable to children.
> > The thing I just want to exclude from the equation is > > the design of REBOL. > > Unfortunately, as I mentioned above, that requires the > assumption that the design is already perfect.
Just to reiterate my points: It's a matter of scale. Design issues are important, but design matters (and disagreements with), though certainly a passionate thing for creative thinkers such as yourself, are probably less important to the immediate and near term goals of REBOL, the language and the company, than your good old basic bug fixes, docs, and support (and for the company specifically: making products! (-:) --Not meant as a discouragement of you taking issue with REBOL's design where you see fit. I'm prone to being defensive of REBOL's design, but I hope I don't come off as prejudiced in my opinions. I also hope I haven't misrepresented anything in my response here. I appreciate all the effort you put forward into understanding and pushing for greater understanding of REBOL. Salutations! -jeff

 [24/32] from: joel:neely:fedex at: 30-Oct-2000 8:09


Hi, Jeff, [rebol-bounce--rebol--com] wrote:
> [ . . . much snipping and it's still like 8 pages . . . ] > I have to write code, dear sir! ...
So do I, unfortunately ;-) Factors in the length are my desire not to sound curt, and to fully explain first time around. If we can waive those issues, I'll be brief(er)... ;-)
> > I hope these essays are serving such an end. > > Certainly if people (newbies especially) manage to read > through these huge ol' emails! :) I think there's something > to the idea of trying to accomplish too many things in one > single email. There should be an automatic send cut off > switch when an email has covered, say, 5 concepts. :)
Fair point. I'll try to do so. -jn-

 [25/32] from: joel:neely:fedex at: 30-Oct-2000 8:20


[rebol-bounce--rebol--com] wrote:
> ... forskip has the same behavior as forall, leaving the series > pegged ... This is by design... > Some people find foreach pegging the series to be disconcerting, > but it is a rather core aspect of REBOL, and it has a valid > reason why it works that way. >
Please, instead of repeating "There's a reason", say what it is! Is there really some benefit to the user to have the block left that way (as in my description of append vs. change), or does this just make life easier for the implementor? We all know that forall works on a series, while foreach extracts values from the series; that doesn't mean that the series can't be put back where it was. (First "good manners" rule learned in kindergarten: "Put it back when you're through playing with it!") It's not disconcerting... it's inconsistent and takes time/effort to write forall foo [...] foo: head foo or (if there's a reason why foo is not at the head) foo2: foo forall foo [...] in almost every case! I'd bet that the overwhelming majority of cases where foo is subsequently used have to resort to one or the other of these workarounds, rather than having foo at end being the most natural setup for what follows. I'm perfectly willing to accept correction on this point from other users, but that's been my experience. -jn-

 [26/32] from: jeff:rebol at: 30-Oct-2000 5:40


Howdy, Joel:
> > ... forskip has the same behavior as forall, leaving the > > series pegged ... This is by design... Some people find
<<quoted lines omitted: 4>>
> Please, instead of repeating "There's a reason", say what > it is!
I did in the parts you elided! :) Here's one example: foo: [1 2 3 4 5 6 7 8] forall foo [ if 1 = random length? foo [break] ] insert x 12 Forall may leave before the end. You will destroy information if you always reset the series to the head. Also, the series may not originally start at the head, so you'll need to restore it wherever it started. Add to that that the series may be changed, so the original location may no longer be a valid location, or it may be not the 'original' position. So you have a number of complicating scenarios that don't make it as simple as "reseting the series when done". So, to reiterate: 1) Preservation of information (forall may leave before completing) 2) Complexity of inputs and outputs. And of course, to understand the how come FORALL works that way: do SOURCE FORALL.
> Is there really some benefit to the user to have the block > left that way (as in my description of append vs. change), > or does this just make life easier for the implementor?
Yes. No (see above).
> We all know that forall works on a series, while foreach > extracts values from the series; that doesn't mean that the > series can't be put back where it was. (First "good > manners" rule learned in kindergarten: "Put it back when > you're through playing with it!")
Problem with changing indexes...
> It's not disconcerting... it's inconsistent and takes > time/effort to write > > forall foo [...] foo: head foo > > or (if there's a reason why foo is not at the head) > > foo2: foo forall foo [...]
A good suggestion people have offered is a refinement to forall that would reset the series when done: forall/reset series -jeff

 [27/32] from: joel:neely:fedex at: 30-Oct-2000 10:59


Hi, Jeff, [rebol-bounce--rebol--com] wrote:
> > > > Please, instead of repeating "There's a reason", say what > > it is! > > I did in the parts you elided! :) >
Obviously, as a Bear of Small Brain, I need more explicit examples.
> Here's one example: > foo: [1 2 3 4 5 6 7 8]
<<quoted lines omitted: 4>>
> Forall may leave before the end. You will destroy > information if you always reset the series to the head.
OK. I can understand that, and accept it as a rationale for having a difference in behavior. Thanks! (As I'm probably not the only Bear of Small Brain in the Hundred-REBOL Woods, this explanation of why there's a difference would make a good addition to the docs.)
> A good suggestion people have offered is a refinement to > forall that would reset the series when done: > > forall/reset series >
That sounds -- in context ;-) -- like a good compromise. Thanks! -jn- -- ; Joel Neely [joel--neely--fedex--com] 901-263-4460 38017/HKA/9677 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 ]

 [28/32] from: holger:rebol at: 30-Oct-2000 9:08


On Thu, Oct 26, 2000 at 10:25:28PM -0500, Joel Neely wrote:
> I'm sure that all of us who have written and deployed code for a > living can understand schedule pressure. Speaking for myself, > THANK YOU VERY MUCH! for taking the time to respond. I really > cannot emphasize enough how much I appreciate having someone from > RT giving us the benefit of your perspective on these issues!
My pleasure :)
> (May I respectfully reserve the right to disagree, however? ;-)
Sure.
> However, I think it a bit hasty to dismiss the issues discussed in > this thread as being primarily due to "switching pain".
I did not intend to dismiss ALL of the issues to switching pain. My comment was mostly directed at Ladislav's interpretation of something being "self-modifying code".
> We would, I believe, all concur with the statement in R/CUG > (page 3-3) that, "How information is processed determines whether > it is code or data."
Yes, although to me there is more to it than that. There is not just this technical side (the application of the von Neumann model to high-level languages), along with the requirements for syntax. There is also the semantic side of it. Most languages clearly separate data from code. If you want to do something "dynamically", i.e. determine the flow of control and the executed code at runtime, then you need to resort to very awkward workarounds, often involving state machines, which keep the "code" very generic, and put the actual semantics into the "data". If you have ever looked at the output of lex/flex or yacc/bison you know what I mean :). Compared to that REBOL not just allows but actually encourages the dynamic creation of "items" (for lack of a better word, meaning to include classical notions of "code", "data" etc.). "Items" can be very "code-like" (the standard REBOL dialect), very "data-like" (e.g. a block of numbers), or anywhere in between. For instance you could dynamically create a block which is then fed to 'parse. The dialect of parse (basically a grammar description with some code-like additions) can be as "data-like" or "code-like" as you want -- or need in any particular situation. Now, would you consider such a block to be code or data ? :-) To me one of the more fascinating qualities of REBOL is how that distinction between "code" and "data" gets blurred, which is why I am very reluctant to distinguish between the two. In particular the rule taught in every introductory Assembly language class "self-modifying code is BAD, cute maybe, but still bad", for good reasons (interference with caching, pipelining etc.) should not be applied to REBOL, as long as the modification is done correctly. Correctly means: you can modify anything except for the block which is just being evaluated. More about this in a separate mail. It does not matter whether what you modify looks like "code" or "data", as long as you follow that simple rule.
> [...] do, in fact, have consequences on the data within the interpreter > and/or on the environment (file system, etc.) If backed into the
<<quoted lines omitted: 3>>
> the shift key (and REBOL is case-insensitive anyway) so I trust > that we all can accept "code" as equivalent to "CODE". ;-)
*grin*. Now repeat this trick in, say, 20 different foreign languages and I'll be really impressed :-).
> Again, the further we go down this list, the more we have to shift > our focus to the concepts, notation, specifications, implementation, > and documentation of the language (and away from the programmer) as > the causes and remedies of the frustration.
Agreed. I am not commenting in detail, because Jeff already did.
> Now, notice that we aren't allowed to compare logic! values????
YES ! And that is the key to it :-). See below.
> This seems consistent with the forbidden comparison that would > apparently have "false" less than "true".
Forbidden comparison : yes. "false" less than "true": no, that is a rather hasty conclusion.
> So, we can't compare logic! values, apparently due only to some > type-checking rule in the comparison operators.
No. We cannot compare logic! values by magnitude because no ordering has been defined for the logic! value domain. We CAN compare logic! values for (in)equality.
> But if we could,
*grin*. Sounds like you are reaching here.
> the way they inter-convert with integer! values would lead us to > conclude that FALSE precedes TRUE. But that is inconsistent with > the fact that the TRUE-th position of a block precedes the FALSE-th > position of the block!
You are implying that whenever there exists a conversion between two data types, and for both of them an ordering has been defined, that the orderings of both data types are equivalent, i.e. the relative position of any two items should remain the same after the conversion. This assumption is, from experience, not always true. For instance consider the domains "signed integer" and "unsigned integer", of equal bit length, in C ("int" and "unsigned int" for example): int as=0, bs=-1; unsigned int au,bu; (as>bs)?TRUE:FALSE; -> TRUE au=(unsigned int)as; bu=(unsigned int)bs; // On a 32-bit machine this becomes 0xffffffff (au>bu)?TRUE:FALSE; -> FALSE Here the ordering is not preserved. And it is an even stronger case than the integer!/logic! case in REBOL, because the type conversion between int and unsigned int is bijective, whereas conversion between integer! and logic! is not. To me the behavior in C does not indicate any inconsistency, and neither does the one in REBOL. The fact that REBOL does not allow you to perform magnitude comparisons on logic! values should be enough of an indication that in the user model REBOL uses logic! values do not really HAVE a magnitude :-). That's why IMHO the behavior of 'to-integer vs. 'pick is not inconsistent. Both are implemented to be as intuitive as possible, as Jeff already explained. 'to-integer returns the integers most users would expect to be associated with boolean values (1 for true and 0 for false). 'pick is consistent with 'either in that 'true precedes 'false. I don't disagree with you that there may very well inconsistencies in REBOL (and if so, we need to think about resolving them). I just don't think that the example you gave illustrates one such case. -- Holger Kruse [holger--rebol--com]

 [29/32] from: joel:neely:fedex at: 30-Oct-2000 16:45


Hi, Holger, I'm skipping your interesting comments on vNL and self-referential code for the purpose of this post (in interest of brevity). I must say, in passing, that I found them quite interesting and worthy of careful reading and thought. [rebol-bounce--rebol--com] wrote: [in a different order...]
> I don't disagree with you that there may very well inconsistencies in > REBOL (and if so, we need to think about resolving them). I just don't > think that the example you gave illustrates one such case. > No. We cannot compare logic! values by magnitude because no ordering > has been defined for the logic! value domain. We CAN compare logic! > values for (in)equality.
Actually, REBOL *has already* defined such an ordering. Consider
>> sort reduce [on off no yes true false]
== [false false false true true true] Clearly SORT thinks that FALSE < TRUE. Isn't it reasonable to ask that < <= > and >= to be allowed to agree with SORT?
> You are implying that whenever there exists a conversion between two > data types, and for both of them an ordering has been defined, that
<<quoted lines omitted: 3>>
> consider the domains "signed integer" and "unsigned integer", of > equal bit length, in C ("int" and "unsigned int" for example):
A few observations in response: 1) Mistakes in the design of C be on the heads of its designers! Just because they made some bad choices doesn't mean REBOL has to. ;-) 2) Typecasting a bit pattern to reinterpret the bits is not the same as a true conversion, in which there is a semantic identification of the actual values. Once we start playing low-level bit-pattern games, we can come up with all sorts of oddities. One of the virtues of REBOL, IMHO, is that it is a high-level language that keeps us out of the bit-swamp. 3) I didn't say "whenever". Any of us can easily define mappings in which the concepts of order change from one interpretation to the other. For instance 1 < 2 < 12, but "1" < "12" < "2". However, in such cases, 3a) There are usually multiple ways to do the conversion, depending on the actual task at hand -- one still can obtain 1 < 2 < 12 and " 1" < " 2" < "12" by choosing to use a conversion that DOES retain order. 3b) The very concept of ordering may be application dependent, or may be a compromise of other forces. The "asciibetical order" commonly used for ordering strings works well for words in a dictionary, but poorly for alphabetizing street addresses. REBOL doesn't supply multiple ways to convert between integer! and logic! data, nor is it obvious why such would be needed. -jn- -- ; Joel Neely [joel--neely--fedex--com] 901-263-4460 38017/HKA/9677 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 ]

 [30/32] from: lmecir:geocities at: 4-Nov-2000 8:31


Hi, Holger Kruse tried to explain his notion of what is legal/defined in Rebol: ...
> > a: [append a [+ 1] 1] > > The above expression modifies a block while it is being evaluated. AFAICT
at
> this time the result of such an operation should be considered "undefined"
and
> "implementation-dependent". In other words: don't do that !
....
> A "legal" way to rewrite the above REBOL expression would, e.g., be: > > a: [append last a [+ 1] do [1]] > > This way the main block does not get changed during evaluation. Only the > block referenced from the main block (the one initialized to be [1]) gets > changed. That is completely legal. > > I hope you can see now why I dislike the term "self-modifying code". There
is
> conceptually a huge difference between the first and second example, and > the term "self-modifying code" tries to be a catch-all phrase that
encompasses
> (and discourages) within the same category both legal (and potentially
useful)
> and illegal practices. > > -- > Holger Kruse > [holger--rebol--com]
Here is my example of a non-WYSIWYG code modifying a sub-block: l: 30 f: does [l: l - 1 insert back tail last second :f [+ 1] if l > 0 [f print 1]] the result: 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 15 14 14 14 14 14 14 14 7 6 5 4 3 2 Moreover, I realize, that my definition of WYSIWYG Code needs some polishing... Regards Ladislav

 [31/32] from: joel:neely:fedex at: 4-Nov-2000 9:50


Hi, Ladislav, Holger, and list! [ Sorry for the length, folks! I tried to be as brief as possible, but this is a non-trivial topic, and I have to think in small steps. ] [rebol-bounce--rebol--com] wrote:
> Hi, > > Holger Kruse tried to explain his notion of what is legal/defined > in Rebol:
... i.e., what we can reason about without having access to the implementation details of the interpreter.
> ... > > > a: [append a [+ 1] 1]
<<quoted lines omitted: 11>>
> > Only the block referenced from the main block (the one > > initialized to be [1]) gets changed. That is completely legal.
...and your example seems to raise questions about that, but let's remember that evaluation is a sequential process and potentially recursive process. A slight paraphrase of the quoted email (taking out the example) gives us Holger's Rule: An evaluation of a block is undefined if that evaluation causes modification of the same block while an evaluation of that block is underway. Your example (same code, different layout, for benefit of Bears With Small Brain such as I) l: 30 f: does [l: l - 1 insert back tail last second :f [+ 1] if l > 0 [ f print 1 ] ] is actually subject to the same effect -- per the rewording above which talks about "an evaluation" of "a block". The difference is that the block to which the rule applies is not the body of F, but rather the block which is the last element of F. During all (but the chronologically last) evaluations of that block, we have [ f print 1 ... ] - === a modification of THAT block affects the doubly-underlined area during the underlined evaluation of F. Given the recursive design of F, there are in fact MULTIPLE pending continuations which evaluate the modified region of this particular sub-block. I'm going out on a limb, writing this as I'm thinking through the issue, but let's try a little experiment here, and "hide" the printing in a nested block. (I'll also eliminate the one prefix application of '+, which shouldn't matter.) This would give us l: 30 f: does [ l: l - 1 insert tail last last second :f [+ 1] if l > 0 [ f do [ print 1 ] ] ] With this change, we get
>> f
31 ... 31 (I won't bother to display all 29 output lines...) We can simplify the test by eliminating any issues relating to hidden use of the function type by rewriting as explicit blocks, l: 10 f: [l: l - 1 insert tail last f [+ 1] if l > 0 [do f print 1]] do f 11 11 11 11 6 5 4 3 2 versus l: 10 f: [l: l - 1 insert tail last last f [+ 1] if l > 0 [do f do [print 1] ]] do f 11 ... 11 versus (another pitch for execution only of immutable blocks?) f: [l: l - 1 insert tail last f [+ 1] if l > 0 [do copy/deep f print 1]] l: 10 do copy/deep f 9 8 7 6 5 4 3 2 1 We might imagine that the issue is that evaluation proceeds INTO a region of the same block which has been modified during a pending evaluation (i.e., strengthen the test of Holger's Rule to apply only to "forward" modifications). A test of this conjecture (I'd hardly dignify it by calling it a "theory" at this point!) is to flip the loci of evaluation and modification around, something like this... l: 10 f: [print 1 l: l - 1 insert next f [1 +] if l > 0 [do f] ] do f 1 2 3 4 5 6 7 8 9 10 == none
>> f
== [print 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 l: l - 1 insert next f [1 +] if l > 0 [do f] ] We seem to be able to "get away with" that change, but that may only be because (some!) pending evaluations utilize a direct reference to pending expressions rather than a block+offset description, whose meaning would have been changed by the insertion. Now, I suspect that Holger is still cheating a bit. (No offense, Holger, but you're working from a position of more knowledge than we Small-Brained Bears have!) I wonder if his rule might be not conservative enough (e.g., what happens if an interpreter is looking ahead at referenced blocks as well as "in-line" words and scalar values...) Of course, I'd prefer that a knowledgable authority "cheat" and give us a specification which the REBOL interpreter is committed to honoring! Therefore, I'm comfortable with Holger's Rule as a preventer of Monsters. Of course, it's better to design with it in mind (by trying to prevent such cases from arising) than to determine after-the-fact whether it applies (in fact, I suspect it to be as computationally infeasible as the Halting Problem!) -jn-

 [32/32] from: joel:neely:fedex at: 4-Nov-2000 9:59


Hi, Holger, Ladislav, and list, Just a short follow-up quibble on terminology ;-) [rebol-bounce--rebol--com] wrote:
> Holger Kruse tried to explain his notion of what is legal/defined > in Rebol:
<<quoted lines omitted: 8>>
> > discourages) within the same category both legal (and > > potentially useful) and illegal practices.
Based on the analysis/experiments I reported in the previous note, I suggest that the phrase "self-modifying block" is, in fact, a reasonable substitute, for two reasons: 1) The notion of "a block which is currently undergoing an evaluation" is perhaps sharper than "some code..."; 2) It is about usage. If you showed me a bit string and asked "Is this an odd number?" I would interpret your question as meaning "If you USE this bit as a number, is that number odd?" and answer you "yes" or no without worrying about whether some other usage of the same bit string (e.g., as representing a sequence of characters in some source code, as being a fragment of object code for some CPU, as being a TCP/IP packet, etc.) might render the question meaningless. It is in that sense that I think the question "Is Foo a self- modifying block?" is reasonable short-hand (even though the answer may be HIGHLY difficult to produce in some cases ;-). -jn-

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