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

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