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