Problem with try [ open/direct/binary tcp://... ]
[1/30] from: eventi:nyic at: 2-Oct-2000 15:16
;; This fails whenever the host's gone away (OFTEN!!)
;; ** Access Error: Cannot connect to 127.10.176.206.
;; ** Where: Open/direct/binary probe To-url join "tcp://"
;; Isn't try supposed to catch and handle this? If not, how else could I ;;
do it?
Connect: Func [ ip port ] [
Gp: try [
Open/direct/binary probe To-url join "tcp://" [ Ip ":" port ]
]
if not error? gp [
insert gp "GNUTELLA CONNECT/0.4^/^/"
print to-string data: copy gp
]
return gp
]
[2/30] from: petr:krenzelok:trz:cz at: 2-Oct-2000 21:37
----- Original Message -----
From: <[eventi--nyic--com]>
To: <[list--rebol--com]>
Sent: Monday, October 02, 2000 9:16 PM
Subject: [REBOL] Problem with try [ open/direct/binary tcp://... ]
> ;; This fails whenever the host's gone away (OFTEN!!)
>
> ;; ** Access Error: Cannot connect to 127.10.176.206.
> ;; ** Where: Open/direct/binary probe To-url join "tcp://"
>
> ;; Isn't try supposed to catch and handle this? If not, how else could I
;;
> do it?
> Connect: Func [ ip port ] [
<<quoted lines omitted: 3>>
> if not error? gp [
> insert gp "GNUTELLA CONNECT/0.4^/^/"
that's wrong btw. You've just assigned 'gp to the result of try, which in
turn can be error object. What about:
if not error? try [gp: open/direct/binary probe join tcp:// [ip ":" port]]
[insert gp "GNUTELLA CONNECT/0.4^/^/"]
note - join knows how to work with datatypes - provide first argument as
url! and it will convert rest for you ... see my code ...
Cheers,
-pekr-
[3/30] from: eventi:nyic at: 2-Oct-2000 16:45
Thanks petr-
It looks like both would work, according to 'help try'...
DESCRIPTION:
Tries to DO a block and returns its value or an error.
TRY is a native value.
gp: try [ open/... ]
and
try [ gp: open/... ]
should be equivalent, no?
Thanks for the sexy join code!!
--e
[4/30] from: al:bri:xtra at: 2-Oct-2000 14:20
e wrote:
> It looks like both would work, according to 'help try'...
> DESCRIPTION:
<<quoted lines omitted: 3>>
> try [ gp: open/... ]
> should be equivalent, no?
You're slightly misreading the description. Have a look at the simple
example below:
>> try [1 / 0]
** Math Error: Attempt to divide by zero.
** Where: 1 / 0
>> error? try [1 / 0]
== true
>> error? err: try [1 / 0]
== true
>> probe err
** Math Error: Attempt to divide by zero.
** Where: 1 / 0
>> err: none
== none
>> error? try [err: 1 / 0]
== true
>> probe err
none
== none
Note that there is a difference.
Andrew Martin
ICQ: 26227169
http://members.nbci.com/AndrewMartin/
http://members.xoom.com/AndrewMartin/
[5/30] from: eventi:nyic at: 2-Oct-2000 17:43
> e wrote:
> > It looks like both would work, according to 'help try'...
<<quoted lines omitted: 5>>
> >
> > should be equivalent, no?
Let me follow with what I think is going on, and you can correct me...
> You're slightly misreading the description. Have a look at the simple
> example below:
>
> >> try [1 / 0]
> ** Math Error: Attempt to divide by zero.
> ** Where: 1 / 0
This actually reduces to an error, no surprise
> >> error? try [1 / 0]
> == true
> >> error? err: try [1 / 0]
> == true
as demonstrated here...
But if I didn't catch the error with 'error? it would ab-end?
> >> probe err
> ** Math Error: Attempt to divide by zero.
<<quoted lines omitted: 6>>
> none
> == none
I think I get it... When the expression is reduced down, if the result is
type error!, it ab ends. So I could have said:
if not error? try [gp: open/direct/binary probe join tcp:// [ip ":" port]] [
insert gp "GNUTELLA CONNECT/0.4^/^/"]
And indeed, it works... Man this language is addictive!!!
[6/30] from: al:bri:xtra at: 2-Oct-2000 15:00
e wrote:
> I think I get it... When the expression is reduced down, if the result is
type error!, it ab ends.
Not quite as the error can be assigned to a variable. It's when Rebol
evaluates the results and it's of error! type. For example:
>> error? err1: err2: err3: try [1 / 0]
== true
>> probe disarm err2
make object! [
code: 400
type: 'math
id: 'zero-divide
arg1: none
arg2: none
arg3: none
near: [1 / 0]
where: none
]
Note that 'err1, 'err2 and 'err3 are all set to the same value and Rebol
doesn't "ab end". It's when the error is evaluated by Rebol:
>> err2
** Math Error: Attempt to divide by zero.
** Where: 1 / 0
that the error causes the "ab end".
I hope that clears things up.
Andrew Martin
ICQ: 26227169
http://members.nbci.com/AndrewMartin/
http://members.xoom.com/AndrewMartin/
[7/30] from: rebol:svendx:dk at: 3-Oct-2000 0:45
Hello [eventi--nyic--com],
Actually you can almost use your original function, you just got to be carefull not to
evaluate the error! value.
(As Andrew pointed out).
Some REBOL values defaults to being evaluated on retrival, functions and errors are examples.
Others don't - blocks, etc.
to safely pass error values around you could use a colon in front of the word.
Here's a slightly modified Connect:
Connect: Func [ ip port ] [
error? Gp: try [
Open/direct/binary probe To-url join "tcp://" [ Ip ":" port ]
]
if not error? :gp [
insert gp "GNUTELLA CONNECT/0.4^/^/"
print to-string data: copy gp
]
return :gp
]
Note the additional colons and the error? in the second line.
Now you can do stuff like:
## error? connect "localhost" 1000
tcp://localhost:1000
== true
BTW.
I once started on a REBOL implementation of the Gnutella protocol, so if you're interested
in some marshalling functions, etc - just let me know...
Best regards
Thomas Jensen
On 02-Oct-00, [eventi--nyic--com] wrote:
[8/30] from: eventi:nyic at: 2-Oct-2000 22:58
>Hello [eventi--nyic--com],
>
>Actually you can almost use your original function, you just got to be
>carefull not to evaluate the error! value.
It made sense once I thought about it a bit.
>BTW.
>I once started on a REBOL implementation of the Gnutella protocol, so if
>you're interested in some marshalling functions, etc - just let me know...
I'm having fun with mine, thank you, but I'll let you know if I get stuck ;)
Too bad Gnutella's sucking so bad lately...
I'm finding that REBOL's a pretty nice tool to get close to the protocol.
Out of curiosity, how do you reverse byte order, for things like the length
header? And I havn't seen an elegant way to concatenate binary! type data.
Join would be nice.
>> probe join #{012345} [ #{0123} ]
#{012345237B303132337D}
>> ; This makes me cringe
>> probe to-binary join to-string #{012345} [ to-string #{6798} ]
#{0123456798}
== #{0123456798}
[9/30] from: al:bri:xtra at: 2-Oct-2000 20:55
eventi wrote:
> And I havn't seen an elegant way to concatenate binary! type data.
> Join would be nice.
>
> >> probe join #{012345} [ #{0123} ]
> #{012345237B303132337D}
Have you tried 'join? Like this:
>> join #{012345} #{0123}
== #{0123450123}
It seems simpler to me. BUT! It also looks to me that you've found a bug in
rebol, that seems odd.
Andrew Martin
ICQ: 26227169
http://members.nbci.com/AndrewMartin/
http://members.xoom.com/AndrewMartin/
[10/30] from: larry:ecotope at: 2-Oct-2000 20:47
Hello [eventi--nyic--com],
This will get you started:
>> a: #{012345} b: #{6789}
== #{6789}
This will reverse the bytes, you need head because reverse leaves series
pointer at the tail.
>> head reverse a
== #{452301}
And this puts it back.
>> head reverse a
== #{012345}
To "join" two binary values.
>> append a b
== #{0123456789}
>>
Cheers
-Larry
You wrote:
[11/30] from: g:santilli:tiscalinet:it at: 3-Oct-2000 9:35
[eventi--nyic--com] wrote:
> Connect: Func [ ip port ] [
> Gp: try [
<<quoted lines omitted: 6>>
> return gp
> ]
You need to write it this way:
Connect: Func [ ip port ] [
either not error? Gp: try [
Open/direct/binary probe To-url join "tcp://" [ Ip
:
port ]
] [
insert gp "GNUTELLA CONNECT/0.4^/^/"
print to-string data: copy gp
gp
] [
disarm gp
]
]
(You'll get an object as result if there was an error; I think
this is what you intended with the code above. Notice that you
cannot return an error without disarming it into an object.)
HTH,
Gabriele.
--
Gabriele Santilli <[giesse--writeme--com]> - Amigan - REBOL programmer
Amiga Group Italia sez. L'Aquila -- http://www.amyresource.it/AGI/
[12/30] from: g:santilli:tiscalinet:it at: 3-Oct-2000 9:46
[eventi--nyic--com] wrote:
> gp: try [ open/... ]
> and
> try [ gp: open/... ]
>
> should be equivalent, no?
They are. Look:
>> try [wrong-code]
** Script Error: wrong-code has no value.
** Where: wrong-code
>> err: try [wrong-code]
** Script Error: wrong-code has no value.
** Where: wrong-code
The word 'ERR is successfully set to the error, too:
>> type? err
== error!
>> err
** Script Error: wrong-code has no value.
** Where: wrong-code
But as you see, as soon as an error value is caught by the
interpreter, it fires; if you don't want it to explode you have to
disarm it with the DISARM function, or consume it with some
function that is able to take an ERROR! as an argument (like
ERROR?, or TYPE? above).
>> error? err
== true
>> probe disarm err
make object! [
code: 300
type: 'script
id: 'no-value
arg1: 'wrong-code
arg2: none
arg3: none
near: [wrong-code]
where: none
]
> Thanks for the sexy join code!!
You could avoid JOIN at all. Use OPEN like this:
Open/direct/binary [scheme: 'tcp host: ip port-id: port]
(Also note that TCP ports are always /DIRECT, so you don't need to
use that refinement.)
HTH,
Gabriele.
--
Gabriele Santilli <[giesse--writeme--com]> - Amigan - REBOL programmer
Amiga Group Italia sez. L'Aquila -- http://www.amyresource.it/AGI/
[13/30] from: g:santilli:tiscalinet:it at: 3-Oct-2000 9:58
[eventi--nyic--com] wrote:
> >> probe join #{012345} [ #{0123} ]
> #{012345237B303132337D}
This is a bug in INSERT.
>> join #{0123} #{0123}
== #{01230123}
>> head insert tail copy #{0123} #{0123} ; eq. to join
== #{01230123}
but:
>> head insert tail copy #{0123} [#{0123}]
== #{0123237B303132337D}
INSERT shouldn't use FORM when inserting a BINARY!. I'm sending
this to feedback too (I don't remember if I had already signaled
this to feedback...).
HTH,
Gabriele.
--
Gabriele Santilli <[giesse--writeme--com]> - Amigan - REBOL programmer
Amiga Group Italia sez. L'Aquila -- http://www.amyresource.it/AGI/
[14/30] from: rebol:techscribe at: 3-Oct-2000 3:14
Hi Andrew,
as an aside:
[Al--Bri--xtra--co--nz] wrote:
> >> probe err
> ** Math Error: Attempt to divide by zero.
> ** Where: 1 / 0
remeber to disarm errors before you probe them. Try
>> probe disarm err
That's probably what you intended to do?
Elan
[15/30] from: joel:neely:fedex at: 3-Oct-2000 6:48
Errmm... I'm not so sure it's a bug!
[Al--Bri--xtra--co--nz] wrote:
> eventi wrote:
> > And I havn't seen an elegant way to concatenate binary! type data.
<<quoted lines omitted: 8>>
> a bug in rebol, that seems odd.
>> foo: probe join #{012345} [ #{0123} ]
#{012345237B303132337D}
== #{012345237B303132337D}
OK, that's the odd-looking thingie. What does it actually contain?
>> foreach c foo [
[ use [cc] [
[ cc: to-char c
[ print [c "^-" enbase/base to-string cc 16 "^-" cc]
[ ]
[ ]
1 01
35 23 #
69 45 E
35 23 #
123 7B {
48 30 0
49 31 1
50 32 2
51 33 3
125 7D }
So, it appears that join took the content of the block, converted it
to a string, and concatenated the bytes of the string onto the binary
value that was its first argument, and gave back that result.
Compare and contrast with
>> join #{32} [1234]
== #{3231323334}
and
>> foo: probe join #{012345} #{0123}
#{0123450123}
== #{0123450123}
-jn-
[16/30] from: joel:neely:fedex at: 3-Oct-2000 7:22
It appears that using a block as the second argument, when the first
argument is of any-string! type, causes the contents of the block
be treated as strings.
>> join #{} ['foo 123 :fum]
== #{666F6F3132333F66756E6374696F6E3F}
>> to-string join #{} ['foo 123 :fum]
== "foo123?function?"
[giesse--dsiaq1--ing--univaq--it] wrote:
[snip]
> INSERT shouldn't use FORM when inserting a BINARY!. I'm sending
> this to feedback too (I don't remember if I had already signaled
> this to feedback...).
>
Do you mean "shouldn't" in the sense of
1) "contradicts the official specification", or
2) "doesn't do what I expected", or
3) "doesn't seem to me to do The Right Thing"?
Please understand, I'm not criticizing your remark! It's just that
option (1) requires that there BE an accessible specification. If
there is one which I've overlooked, I'll be VERY grateful if you
will tell me where it is (and it may very well be Carl's massive
tome of yesterday evening -- I just haven't finished reading it!)
Of course, I generally hold your experienced expectations -- as
in (2) -- or your good taste in programming -- as in (3) -- in
very high regard!
-jn-
[17/30] from: joel:neely:fedex at: 3-Oct-2000 8:57
Just a brief follow-up after checking the new docs...
[giesse--dsiaq1--ing--univaq--it] wrote:
[...code samples snipped...]
> INSERT shouldn't use FORM when inserting a BINARY!. I'm sending
> this to feedback too (I don't remember if I had already signaled
> this to feedback...).
>
Page 7-3 (225 of 574) begins by saying
This chapter will introduce functions that convert REBOL values
into strings. These functions are used often ... They include:
followed by Table 7-2, titled "String Conversion Functions". In
that table join is described as "convert values with no spaces".
Later on, page 7-4 (226 of 574) states:
The join function takes two arguments and concatenates
them into a single series.
The data type of series returned is based on the value of the
first argument. When the first argument is a series value,
that series type is returned.
and then proceeds to give examples of computing string! , file! ,
and url! values via join invocations.
The next page goes on to say:
When the first argument is not a series, the join converts
it to a string first, then performs the append.
Finally the following page (7-6, 228 of 574) says
When the second argument to join is a block, the values of
that block are evaluated and appended to the series returned.
This is not a smack-in-the-kisser blunt as I would like, but I
think it means this (*WARNING* *MY NON-OFFICIAL READING*):
Type of Type of Type of
1st arg 2nd arg result Treatment of args
----------- ----------- ----------- --------------------------
1) any-string! not block! arg 1 type arg 2 value converted to
string and appended to
copy of arg 1 value
2) any-string! any-block! arg 1 type arg 2 values converted to
strings and appended to
copy of arg 1 value
3) not
any-string! any string! join to-string arg1 arg2
(now guaranteed to be one
of the first two cases)
If I'm correct, the behavior with a binary! first argument is
actually the intended, documented behavior, as the binary! type
is subsumed under the any-string! type.
-jn-
P.S. "I'm not a language lawyer, but I play one on the 'Net!" ;-)
Isn't it fun having a spec?!?!
[18/30] from: al:bri:xtra at: 3-Oct-2000 12:05
> Hi Andrew,
Hi, Elan.
> as an aside:
> [Al--Bri--xtra--co--nz] wrote:
<<quoted lines omitted: 4>>
> >> probe disarm err
> That's probably what you intended to do?
No, I meant to write exactly that, to show that _evaluating_ the error in
err is where Rebol "ab ends" as it were, rather than where it "passed out
the left end" as it were.
>> probe err
Flow------>+
v
<--+
========^== ; Error occurs at ^ point
The error occurs after 'err is evaluated, as Rebol prepares to enter the
value of 'err as an argument for 'probe.
Andrew Martin
ICQ: 26227169
http://members.nbci.com/AndrewMartin/
http://members.xoom.com/AndrewMartin/
[19/30] from: rebol:svendx:dk at: 3-Oct-2000 23:24
Hello [eventi--nyic--com],
On 03-Oct-00, [eventi--nyic--com] wrote:
-- snip --
> I'm having fun with mine, thank you, but I'll let you know if I get stuck ;)
ok :)
> Too bad Gnutella's sucking so bad lately...
yep
> I'm finding that REBOL's a pretty nice tool to get close to the protocol.
> Out of curiosity, how do you reverse byte order, for things like the length
<<quoted lines omitted: 6>>
> #{0123456798}
> == #{0123456798}
yeah, REBOLs build-in binary capabilities are a bit strange sometimes :-/
## to binary! 100
== #{313030}
(same as: to binary! "100")
I use these functions:
int-to-bin: func [int /little-endian /local bin] [
bin: load join "#{" [to-hex int "}"]
either little-endian [
head reverse bin
] [
bin
]
]
bin-to-int: func [bin /little-endian /local int] [
bin: either little-endian [
head reverse copy bin
] [
bin
]
int: (bin/1 * (2 ** 24)) + (bin/2 * (2 ** 16)) + (bin/3 * (2 ** 8)) + bin/4
]
Best regards
Thomas Jensen
[20/30] from: carl:rebol at: 3-Oct-2000 16:38
>> And I havn't seen an elegant way to concatenate binary! type data.
>> Join would be nice.
>>
>>>> probe join #{012345} [ #{0123} ]
>> #{012345237B303132337D}
Works for:
>> join #{ABCD} #{0123}
== #{ABCD0123}
But:
>> join #{ABCD} [ #{0123} ]
== #{ABCD237B303132337D}
Now that seems like a bug according to the definition of join.
>yeah, REBOLs build-in binary capabilities are a bit strange sometimes :-/
>## to binary! 100
>== #{313030}
Doesn't that seem like a bug?
[21/30] from: brett:codeconscious at: 4-Oct-2000 11:02
Hi Joel,
Regarding your two messages in response to Gabriele's reasonable assertion
that
INSERT shouldn't use FORM when inserting a BINARY!
You seem to have misunderstood Gabriele to be talking about JOIN where I
believe the statement was simply about INSERT. Maybe it was because you
missed this snippet of the message:
>> head insert tail copy #{0123} #{0123} ; eq. to join
== #{01230123}
but:
>> head insert tail copy #{0123} [#{0123}]
== #{0123237B303132337D}
I would have expected that both lines should have the same result.
The second example is equivalent to
>> head insert tail copy #{0123} form #{0123}
== #{0123237B303132337D}
Converting the value argument of insert clearly looks erroneous.
Brett.
PS I've enjoyed reading your recent investigations into the language and
performance of Rebol. With a little time, I'll be re-reading them. Keep up
the good work!
[22/30] from: larry:ecotope at: 3-Oct-2000 18:24
Hi Carl
> >yeah, REBOLs build-in binary capabilities are a bit strange sometimes :-/
> >## to binary! 100
> >== #{313030}
>
> Doesn't that seem like a bug?
>
Yes, now that you mention it, this has bothered me all along. It is often
frustrating that to-binary returns the ascii bytes of the string
representation of integers and decimals. It would be useful and logical if
to-binary actually returned the IEEE754 representations for integer and
decimal arguments. This would be very helpful for those few of us who are
interested in using REBOL for engineering and scientific numeric work.
Possibly things are the way they are because the binary type is an
any-string. I guess the internal representation for the data of a binary is
just the same sequence of bytes as a string. Consider
>> to-string to-binary 100
== "100"
>> to-binary 100
== #{313030}
>> to-binary to-string #{313030}
== #{313030}
>> to-string #{313030}
== "100"
In other words, to-binary and to-string are mutually inverse operations.
They just show different ways of looking at the same underlying byte
sequence.
Cheers
-Larry
[23/30] from: kgd03011:nifty:ne:jp at: 4-Oct-2000 11:34
Hi Joel,
I think there's a big problem when the docs use a vague term like "convert
to a string," since there are several ways that values may be converted to
strings.
As Andrew Martin noted, the cause of the strange behavior seen by JOIN is
INSERT's behavior (also seen with APPEND).
It seems that INSERT uses FORM to convert values contained in blocks before
inserting them, and TO STRING! to convert values which aren't contained in
blocks. Most of the time the results of TO STRING! and FORM are the same, but
there are several differences.
>> to+form: func[v][print to string! :v print form :v]
>> to+form pi
3.14159265358979
3.14159265358979
>> to+form %file.ext
file.ext
file.ext
>> to+form to binary! "binary"
binary
#{62696E617279}
>> to+form first [abc/def/ghi]
abcdefghi
abc/def/ghi
>> to+form ["abc" "def" "ghi"]
abcdefghi
abc def ghi
These differences correspond exactly to the behavior of INSERT, APPEND and
JOIN:
>> append "" to binary! "binary"
== "binary"
>> append "" reduce [to binary! "binary"]
== "#{62696E617279}"
>> append "" first [abc/def/ghi]
== "abcdefghi"
>> append "" [abc/def/ghi]
== "abc/def/ghi"
>> append "" ["abc" "def" "ghi"]
== "abcdefghi"
>> append "" [["abc" "def" "ghi"]]
== "abc def ghi"
Eric
[24/30] from: joel:neely:fedex at: 3-Oct-2000 23:31
Hi, Brett...
You're right about my JOIN vs. INSERT confusion. I think the thread
had earlier been discussing
join #{012345} [#{0123}]
(or some similar expression) and my caffeine-deprived brain didn't
catch Gabriele's shift of verbs. Thanks for catching it!
However, I think my real point survives my poor application of it;
I wasn't disagreeing with Gabriele at all! I was trying to use the
issue of modifying binary! values (whether with join or insert )
to illustrate the historical need we've had for clear and unambiguous
specifications for what Carl (and the rest of the RT folks) INTENDED
for REBOL to do in various situations.
If I find myself surprised by something that REBOL does, I'm quite
willing to accept that I may just not know enough for my predictions
to be accurate all the time. But when someone with the experience
and expertise of Gabriele hits a surprise, I have to wonder whether
1) the implementors INTENDED for REBOl to do what it does, but the
reasoning is not sufficiently obvious nor well-documented enough
to make the intent (and motivations) clear, or
2) there wasn't a specific intent here, just a happenstance of the
way it got implemented, without anyone desiring or foreseeing
the surprise that some folks got, or
3) it's a real bug, in that REBOL is specifically doing something
different than intended by Carl (and the rest of the RT folks).
Absent a clearly-stated specification, all ANY of us can do (even
the most expert -- of whom I consider Gabriele to be one) is submit
a feedback/support email and wonder.
-jn-
[brett--codeconscious--com] wrote:
[25/30] from: joel:neely:fedex at: 4-Oct-2000 0:07
[KGD03011--nifty--ne--jp] wrote:
> Hi Joel,
>
> I think there's a big problem when the docs use a vague term like
> "convert to a string," since there are several ways that values
> may be converted to strings.
>
You're absolutely correct, IMHO!
One of the things we need the docs to do is explain WHY there are
so many ways and when (as your examples below so well point out)
each is used. The distinction between form and mold IS
addressed in the docs (and -- fortunately! -- none of the confusing
cases seem to have mold as part of the picture).
I don't think I've mentioned this analogy before on the list, but
some aspects of REBOL (such as discussed in the "series" thread of
several months back) make me feel as if I'm doing nuclear physics
while trying to learn, use, and explain REBOL. I take it on faith
that there are some interesting particles in there, but I have no
way to perceive them with the naked eye, nor have I had a spec that
told me what they were doing. So, it became necessary to draw on
my experience with other languages to imagine what they MIGHT be
doing, and then to think up experiments that probe those hypotheses
and confirm or refute them.
It is possible (and even fun!) to learn things that way -- I once
figured out the six-stack microcoded implementation of an
Algol-like systems programming language by trying things out and
reading core dumps -- but it certainly isn't the most efficient
nor the easiest on the nerves!
-jn-
[26/30] from: rebol:techscribe at: 4-Oct-2000 2:00
Hi Eric,
you point out that INSERT (and APPEND and JOIN) appear to use FORM to convert
values contained in blocks, whereas they use TO STRING! to convert values which
are not contained in blocks.
If you observe how TO STRING! processes values contained in blocks, you will
find that TO STRING! itself acts no different from FORM with respect to blocks.
If the functions you inspect use nothing but TO STRING! to convert values
(string! or block! values), then the results will be exactly the ones you
observed. Some of your results are unexpected because REBOL does not use a
symmetrical conversion model. The question is, is that good?
[KGD03011--nifty--ne--jp] wrote:
> It seems that INSERT uses FORM to convert values contained in blocks before
> inserting them, and TO STRING! to convert values which aren't contained in
> blocks. Most of the time the results of TO STRING! and FORM are the same, but
> there are several differences.
>
[snipped examples displaying similarities and differences between FORM and TO
STRING!]
> These differences correspond exactly to the behavior of INSERT, APPEND and
> JOIN:
>
> >> append "" to binary! "binary"
> == "binary"
>> to string! to binary! "binary"
== "binary"
> >> append "" reduce [to binary! "binary"]
> == "#{62696E617279}"
Here you conclude that REBOL appears to be using FORM instead of TO STRING!,
because a block is being converted. However, TO STRING! does the exact same
thing:
>> to string! reduce [to binary! "binary"]
== "#{62696E617279}"
> >> append "" first [abc/def/ghi]
> == "abcdefghi"
>> to string! first [abc/def/ghi]
== "abcdefghi"
> >> append "" [abc/def/ghi]
> == "abc/def/ghi"
Again you note the similarity to using FORM. However, TO STRING! acts no
different:
>> to string! [abc/def/ghi]
== "abc/def/ghi"
> >> append "" ["abc" "def" "ghi"]
> == "abcdefghi"
>> to string! ["abc" "def" "ghi"]
== "abcdefghi"
> >> append "" [["abc" "def" "ghi"]]
> == "abc def ghi"
>> to string! [["abc" "def" "ghi"]]
== "abc def ghi"
In all cases TO STRING! behaves as FORM would with respect to blocks.
Nevertheless the results are surprising in some instances. That is because one
would expect that REBOL uses a SYMMETRICAL CONVERSION model, which it doesn't.
What I mean is:
A SYMMETRICAL CONVERSION MODEL in REBOL would look something like this:
1. In REBOL data always occurs in association with a datatype. The combination
of data with a datatype is a value.
2. CONVERSION means disassociating some data from its current datatype and
associating it with a different datatype, while leaving the data unmodified. Its
value representation changes because it is now associated with a different
datatype.
3. CONVERSION INTEGRITY means that converting a value to a different datatype
and back to the original datatype must result in the same value it originally
had. The reason is that the data was not modified during the conversion.
REBOL does not follow this symmetrical conversion model. REBOL's conversion does
not maintain CONVERSION INTEGRITY. REBOL's CONVERSION implementation is not
always symmetrical. This means that in some instances REBOL does not limit
itself to modifying the datatype associated with the data that is being
converted, instead, the data itself is modified as well.
Some of the examples you show show REBOL supporting symmetrical conversion. For
instance
>> binary-block: reduce [to binary! "binary"]
== [#{62696E617279}]
>> to string! binary-block
#{62696E617279}
and
>> to block! to string! binary-block
== [#{62696E617279}]
The final result of converting the string representation of the data contained
in the block binary-block is a block containing the same data. That is
appropriate.
5. I think it is reasonable to expect that REBOL's conversion routines maintain
CONVERSION INTEGRITY. Unfortunately that is not true. Examples:
>> to string! ["abc" "def" "ghi"]
SHOULD RETURN
~~ {"abc" "def" "ghi"}
INSTEAD IT RETURNS
== "abc def ghi"
Therefore
>> to block! to string! ["abc" "def" "ghi"]
results in
== [abc def ghi]
i.e. a block of words, whereas CONVERSION INTEGRITY would require it to return
== ["abc" "def" "ghi"]
a block containing strings, which was what the original block contained.
Another example is
>> to block! to string! [["abc" "def" "ghi"]]
which returns
== [abc def ghi]
Under a symmetrical model this should result in
== [["abc" "def" "ghi"]]
What happens is that during the string conversion [["abc" "def" "ghi"]] was
converted to "abc def ghi", whereas following a symmetrical model it should have
been converted to
{["abc" "def" "ghi"]}. Only the outer block should have been replaced by a
string. Instead the element of the outer block (a block containing three
strings) was stripped, and in addition the three strings contained in the block
were converted to character sequences. Additional space characters were
inserted, separating the character sequences that were originally strings.
Is REBOL's current implementation of ASYMMETRICAL CONVERSION, i.e. a conversion
that permits the modification of the data and does not limit itself to modifying
the representation of the data by associating the data with a different
datatype, more useful - albeit occasionally surprising - than a symmetrical
conversion would be?
[27/30] from: g:santilli:tiscalinet:it at: 4-Oct-2000 15:35
[joel--neely--fedex--com] wrote:
> It appears that using a block as the second argument, when the first
> argument is of any-string! type, causes the contents of the block
> be treated as strings.
Yup, INSERT uses FORM to convert values that are not of the
ANY-STRING! (pseudo)type to strings. The point is, BINARY! values
should not be converted, because you CAN insert binary values in
ANY-STRING!s without conversion. Furthermore,
insert something #{1234}
should (AFAIK) be equivalent to
insert something [#{1234}]
> > INSERT shouldn't use FORM when inserting a BINARY!. I'm sending
> Do you mean "shouldn't" in the sense of
>
> 1) "contradicts the official specification", or
If you have such a thing, please send it to me! ;-)
> 2) "doesn't do what I expected", or
> 3) "doesn't seem to me to do The Right Thing"?
I can't read Carl's mind, but I assume that it wasn't his
intention to have the two expressions above behaving differently.
> there is one which I've overlooked, I'll be VERY grateful if you
> will tell me where it is (and it may very well be Carl's massive
:-)
> tome of yesterday evening -- I just haven't finished reading it!)
I didn't have the time either...
> Of course, I generally hold your experienced expectations -- as
> in (2) -- or your good taste in programming -- as in (3) -- in
> very high regard!
Thank you very much. But I'm sure you're far more experienced than
me in programming. :)
I'm just a student after all,
Gabriele.
--
Gabriele Santilli <[giesse--writeme--com]> - Amigan - REBOL programmer
Amiga Group Italia sez. L'Aquila -- http://www.amyresource.it/AGI/
[28/30] from: joel:neely:fedex at: 4-Oct-2000 8:38
Hello, Elan,
I'd like to suggest a minor tweak in terminology that I think would
help avoid confusion in other contexts than this present problem.
I think it is in harmony with your fundamental point, but extends
it to cover a bit more territory.
Anyone who doesn't want the long discussion is invited to skip ahead
to the heading "THE TROUBLE WITH STRINGS!!!" -- or even the heading
THE PUNCH LINE!!!
, where there are some specific questions about
type conversion confusion.
-jn-
[elan--loop--com] wrote:
[...problem-specific background and examples snipped...
> Nevertheless the results are surprising in some instances.
> That is because one would expect that REBOL uses a SYMMETRICAL
<<quoted lines omitted: 8>>
> leaving the data unmodified. Its value representation changes
> because it is now associated with a different datatype.
This process -- changing the type/interpretation WITHOUT changing
the internal data -- is referred to in other languages as
TYPECASTING or (in the aggressive-abbreviation mentality of c)
simply CASTING.
I'm used to seeing the word "conversion" used in the more general
sense of creating/returning a value of a different type which
is "equivalent" (in a well-defined way!) to the original. This
process may include the necessity of changing the representation.
Consider these examples:
>> fee: {abc}
== "abc"
>> length? fee
== 3
>> fie: to-binary fee
== #{616263}
>> length? fie
== 3
Without knowing the internals of REBOL implementation, one can
still safely say that it's POSSIBLE that this is simply casting,
as there's no logical necessity for the data bits to change, but
only the type bits.
>> foe: #"A"
== #"A"
>> length? foe
** Script Error: length? expected series argument of type:
series port tuple struct.
** Where: length? foe
>> fum: to-string foe
== "A"
>> length? fum
== 1
This would be an example of conversion, where BY DEFINITION there
is information required for a string (e.g., its length) that has
nothing to do with the value of a single character. (And, with
the phrase "by definition" I refer to the inherent concept of
string, not the details of REBOL's implementation of them.)
> 3. CONVERSION INTEGRITY means that converting a value to a
> different datatype and back to the original datatype must
> result in the same value it originally had. The reason is that
> the data was not modified during the conversion.
>
This is an important point. My only suggestion is to call this
TYPECASTING INTEGRITY, so that we can then let the phrase
conversion integrity
(or perhaps "...consistency") cover more
ground. If we read the following remarks with "typecasting"
substituted for "conversion", I think the point is undamaged.
> REBOL does not follow this symmetrical conversion model.
> REBOL's conversion does not maintain CONVERSION INTEGRITY.
<<quoted lines omitted: 4>>
> Some of the examples you show show REBOL supporting symmetrical
> conversion. For instance
[...examples snipped...]
> 5. I think it is reasonable to expect that REBOL's conversion
> routines maintain CONVERSION INTEGRITY. Unfortunately that is
> not true. Examples:
>
[... more examples snipped...]
> Is REBOL's current implementation of ASYMMETRICAL CONVERSION,
> i.e. a conversion that permits the modification of the data
> and does not limit itself to modifying the representation of
> the data by associating the data with a different datatype,
> more useful - albeit occasionally surprising - than a
> symmetrical conversion would be?
>
An an aside to this last point, let me add that inconsistency
has a cost -- it requires extra effort to learn/teach/use the
concepts where inconsistencies emerge. While I'm not saying
that inconsistencies are automatically evil, I would suggest
they are an unnecessary cost unless there's some significant
benefit that offsets that cost. Now, back to the main idea...
Now let's add the idea of "conversion consistency" as follows
(with my numbers an extension of Elan's list):
6. I think it's reasonable to have the idea of canonical (or
"standard", if one prefers) internal and external
representations for each datatype in the language. The basic
conversion mechanism should ensure that when a value is
converted to a different type (assuming, of course, that the
conversion makes logical sense!) that it is converted to the
standard representation.
7. Then we can discuss the idea of "conversion consistency",
in which converting a value to another type (or even through
a chain of such conversions!) then back to the original type
results in a value that is indistinguishable from the original.
Extending the conversion example from above,
>> foe: #"A"
== #"A"
>> fum: to-string foe
== "A"
>> to-char fum
== #"A"
>> foe = to-char fum
== true
>> fum = to-string to-char fum
== true
I suspect that these all make sense to us. But sometimes REBOL
gives us a surprise!
>> phee: 1.1
== 1.1
>> type? phee
== decimal!
>> phie: to-money phee
== $1.10
>> type? phie
== money!
>> to-decimal phie
** Script Error: Invalid argument: $1.10.
** Where: to decimal! :value
>> make decimal! phie
** Script Error: Invalid argument: $1.10.
** Where: make decimal! phie
>> second phie
== 1.1
>> type? second phie
== decimal!
The decimal/money case, to my mind, is very much like the
char/string case, in that going one way we need to add some
information which, going the other way, we need to strip off.
But doing so should be possible, and should give us back an
equivalent value. Note that there may be different options or
representations for some data types, which can also surprise
us:
>> phie/1: "US"
== "US"
>> phie
== US$1.10
>> phie/1: "HK"
== "HK"
>> phie
== HK$1.10
>> type? phie
== money!
...so far no astonishments (and converting any of these to
decimal would seem to make sense -- but would require losing
the added information), but then...
>> phie + $1.50
** Script Error: HK$1.10 not same denomination as $1.50.
** Where: phie + $1.50
...arguably a safety measure that would prevent the same kind of
bug that cost us a Mars probe, but...
>> $1.50 + phie
== $2.60
>> to-money phie
== HK$1.10
...apparently not consistently applied (?) requiring that we
now remember when addition is symmetric and when it is not!!!
>> 1 + 2.5
== 3.5
>> type? 1 + 2.5
== decimal!
>> 2.5 + 1
== 3.5
>> type? 2.5 + 1
== decimal!
>> (1 + 2.5) = (2.5 + 1_
** Syntax Error: Invalid integer -- 1_.
** Where: (line 1) (1 + 2.5) = (2.5 + 1_
>> (1 + 2.5) = (2.5 + 1)
== true
>> ($1.50 + phie) = (phie + $1.50)
** Script Error: HK$1.10 not same denomination as $1.50.
** Where: phie + $1.50
THE TROUBLE WITH STRINGS!!!
Having a string! datatype offers us lots of landmines to step on.
Unlike almost all other types, a string! value can actually be
a value all on its own...
>> phoe: "Hello"
== "Hello"
>> phum: join phoe [", " {world} "!"]
== "Hello, world!"
>> print phum
Hello, world!
...but can also serve as the external representation for values of
other datatypes...
>> a: "1.2"
== "1.2"
>> b: do a
== 1.2
>> type? a
== string!
>> type? b
== decimal!
>> c: "http://www.rebol.com"
== "http://www.rebol.com"
>> d: load c
== http://www.rebol.com
>> type? c
== string!
>> type? d
== url!
This is even trickier, because when I type (pardon the pun ;-)
>> e: 1.2
== 1.2
>> f: http://www.rebol.com
== http://www.rebol.com
>> type? e
== decimal!
>> type? f
== url!
I'm actually typing strings the whole time. REBOL is interpreting
and converting all along the way.
THE PUNCH LINE!!!
Now, the payoff question is this: How do we understand conversion
from one datatype (TYPE1) to another (TYPE2)? There are several
options:
1) The conversion may not make sense, so we barf.
>> to-money (1 = 1)
** Script Error: Invalid argument: true.
** Where: to money! :value
2) We base our conversion on some sort of semantic equivalence,
and can do an exact (invertable) equivalence.
>> to-decimal 13
== 13
>> type? to-decimal 13
== decimal!
Note that in this case the external representation reinforces our
notion of "equivalence" for integer! and decimal! types:
>> to-decimal 13.00000000000000000000
== 13
3) We base our conversion on some sort of partial semantic
equivalence, where the notion of "the best one can do" makes
sense to us.
>> 13.0 = to-integer 13.0
== true
>> to-integer 13.99
== 13
>> 13.99 = to-integer 13.99
== false
Here REBOL is using "discard the fractional part" as the implicit
definition of "the best one can do". Other languages use "round
to the nearest" instead. Either is defensible -- as long as it
is clearly stated -- but it is nice to have an option to use the
non-default choice as well!
>> round: func [x [number!]] [
[ to-integer (x +
[ either x < 0 [-0.5] [0.5])
[ ]
>> round .2
== 0
>> round .7
== 1
>> round -.2
== 0
>> round -.7
== -1
Finally, we have a tricky option!
4) We do our conversion in two steps: TYPE1 -> ??? -> TYPE2
where the "???" represents some other type that is implicit
in the implementation but not asked for by us, and maybe not even
desired!
>> to-binary 100
== #{313030}
>> to-binary 1.25
== #{312E3235}
Here we can infer that the "???" is string! , which is not what
we might have expected (or desired?).
Admittedly there is an open question: given the varieties of sizes
of integer values and the whole "endedness" question, what is the
bitstring representation of one hundred? #{00000064}, #{0064},
#{64}, #{00640000} ??? Is the following REALLY independent of
platform>
>> charset [#"0" - #"9"]
== make bitset! #{
000000000000FF03000000000000000000000000000000000000000000000000
}
BUT, add a dose of inconsistency, and the gravy gets VERY lumpy:
>> to-string "hi"
== "hi"
>> to-binary to-string "hi"
== #{6869}
>> to-binary "hi"
== #{6869}
...no surprise...
>> to-string first ["hi"]
== "hi"
>> to-binary to-string first ["hi"]
== #{6869}
>> to-binary first ["hi"]
== #{6869}
...still no surprise...
>> to-string ["hi"]
== "hi"
>> to-binary to-string ["hi"]
== #{6869}
>> to-binary ["hi"]
** Script Error: Invalid argument: hi.
** Where: to binary! :value
...LUMPY GRAVY! Not only is it a big surprise that the conversion
fails, the error message is highly misleading!
When does it make sense for string! to be an intermediate (and
implicit, at that) type in a conversion between two other types?
If/when it does, which kind of string! should be used -- the
internal one, or the external one?
Shouldn't the rules and rationales for typecasting and conversion
be spelled out in agonizing detail? (And if I've missed it, please
tell me where; I'll be glad to shut up and go read for a while. ;-)
-jn-
[29/30] from: g:santilli:tiscalinet:it at: 4-Oct-2000 16:51
[carl--rebol--com] wrote:
> >yeah, REBOLs build-in binary capabilities are a bit strange sometimes :-/
> >## to binary! 100
> >== #{313030}
>
> Doesn't that seem like a bug?
If you're saying that, then sure it does! :)
It would be really useful to be able to convert an integer (or
even a decimal, perhaps in IEEE format?) to a (32 bit?) binary.
Regards,
Gabriele.
--
Gabriele Santilli <[giesse--writeme--com]> - Amigan - REBOL programmer
Amiga Group Italia sez. L'Aquila -- http://www.amyresource.it/AGI/
[30/30] from: kgd03011:nifty:ne:jp at: 5-Oct-2000 1:15
Hi Elan,
Thanks for setting me straight on where my observations were lacking.
So, apparently INSERT and friends always use TO STRING! , but
TO STRING! 's behavior itself is a little harder to understand than I'd
thought.
I take it that of the three types of conversion, MOLD preserves the
integrity of the original value the most, TO STRING! compromises it the
most, and FORM is somewhere in between. TO STRING! wipes out the
distinction between the members of any block values it gets, but preserves
the internal integrity of individual members of its block value arguments.
This produces these results, which were unexpected for me:
>> to string! [abc/def/ghi jkl/mno/pqr]
== "abc/def/ghijkl/mno/pqr"
>> to string! [["abc" "def" "ghi"] ["jkl" "mno" "pqr"]]
== "abc def ghijkl mno pqr"
I'd like to know if Carl thinks this behavior is a bug as well.
You say,
>5. I think it is reasonable to expect that REBOL's conversion routines
>maintain CONVERSION INTEGRITY. Unfortunately that is not true. Examples:
I was thinking that CONVERSION INTEGRITY was the job of MOLD, which saves
the most integrity. But even that doesn't give quite the right answer
sometimes:
>> to block! mold ["abc" "def"]
== [["abc" "def"]]
although LOAD works OK for this case:
>> load mold ["abc" "def"]
== ["abc" "def"]
I think CONVERSION INTEGRITY can be achieved in most cases, but you have to
use a heterogenous set of tools on a case-by-case basis. Not an ideal
situation!
And it seems there's no way to maintain CONVERSION INTEGRITY with datatype!
>> to datatype! to string! string!
** Script Error: Cannot use to on datatype! value.
** Where: to datatype! to string! string!
>> load mold string!
== string!
>> type? load mold string!
== word!
Eric
Notes
- Quoted lines have been omitted from some messages.
View the message alone to see the lines that have been omitted