r3wp [groups: 83 posts: 189283]
  • Home
  • Script library
  • AltME Archive
  • Mailing list
  • Articles Index
  • Site search
 

World: r3wp

[Core] Discuss core issues

Joe
17-Aug-2007
[8642]
pekr,sqlab, thank you for your answers. I thought that sync ports 
also had a timeout like the async kernel (set-modes port [timeout: 
30]) but they do not. thanks
Henrik
20-Aug-2007
[8643x2]
I may be the last to learn this, but I didn't know you could do this:

>> type? first [none]
== word!
>> type? first [#[none]]
== none!
which is neat :-)
Geomol
21-Aug-2007
[8645]
Any thoughts on this?

>> #"a" * 2
== #"Â"
>> 2 * #"a"
== 194


So multiply is commutative in a 'funny' way. In this example, you 
get the result in the same datatype as the first argument. This also 
works:

>> #"a" * 2.0
== #"Â"

But you can't do:

>> 2.0 * #"a"
** Script Error: Cannot use multiply on decimal! value


So multiply is not commutative, when it comes to decimals and chars. 
Any comments? Also think of other datatypes, you wanna multiply.
Tomc
22-Aug-2007
[8646]
I think I would avoid using that feature  often
[unknown: 5]
29-Aug-2007
[8647]
I once heard that R2 doesn't allow you to free memory and that R3 
will.  Anyone know if we are able to free memory in R2?  I think 
it was concerning a stats command discussion somewhere.
btiffin
30-Aug-2007
[8648x2]
Paul; Check http://www.rebol.org/cgi-bin/cgiwrap/rebol/view-script.r?script=free-mem.r
for a small blurb and a way to free memory in R2.
Newer versions of REBOL allow a simple  unset 'var  recycle  sequence. 
 But GC is voodoo a lot of times, not just in REBOL, so there are 
probably cases that this won't apply.
DanielSz
30-Aug-2007
[8650x2]
Hi Rebol community, I was wondering about how you deal with error 
handling in Rebol in those situations where you have a number of 
network operations which are likely to fail part of the time. Of 
course, you already raised the timeout for network protocols (system/schemes/default/timeout: 
0:10). Now say your script contains a number of network operations 
(read url, for example) wrapped in user functions. This is a very 
common situation.

For example:

fetch-actual-ip: does [

        page: read-url-with-basic-auth http://192.168.1.1/Status.htm(http-basic-auth-key 
        router_username router_password)
	parse page [thru <td class="stdbold" nowrap> copy ip to </td>]
	return to-tuple ip
]


Now this is fine, but if your read operation fails, the script will 
abort. You would like the function to try again a couple of times.


The first thing you would be tempted to do is to redefine the function 
to repeat the function in case of error or unexpected result. A recursive 
function like the next one may provide some solace: 

fetch-actual-ip: does [

        page: read-url-with-basic-auth http://192.168.1.1/Status.htm(http-basic-auth-key 
        router_username router_password)
        either none? tmp: find/tail page "ip address"
                [
                print "Retrying..."
                retries: retries + 1

                if retries > 10 [print "Retried 10 times. Exiting" quit]
                fetch-actual-ip
               ] [

                parse tmp [thru <td class="stdbold" nowrap> copy ip to </td>]
                retries: 0
                return to-tuple ip
                ]
]


You might have a number of user functions and each can fail, so transforming 
every user function into a recursive one with its own error handling 
block can be tedious. 


Wouldn't it be better to abstract the process with a meta-function 
that accepts as input the user function itself? The error handling 
logic would then be exclusively handled by that meta-function, which 
will save you from redundancy if you have multiple user functions. 

Let's do it:


retry: func [external_function [function!] retries [integer!] /local 
.retry [function!] tries [integer!] value] [
	tries: 0
	.retry: func [.external_function [function!]] [
		either not error? try [value: .external_function]
			[
			if not none? value [return value]
			]
			[
			print "Retrying..."
			tries: tries + 1

   if tries > retries [print rejoin ["Tried function " tries " times. 
   Exiting"] return]
			.retry :.external_function
			]
	]
	.retry :external_function
]

Now you call the short version of fetch-actual-ip like this:

retry :fetch-actual-ip 5

(try the user function 'fetch-actual-ip up to 5 times)

Anyone wants to comment or improve on this?
One difficulty is to refine the meta-function 'retry to accept user 
functions with a variable number of arguments. Tricky. Anyone got 
an idea?
Graham
30-Aug-2007
[8652]
provide the argument list in a block to the retry function
Gregg
31-Aug-2007
[8653]
It can seem like more work, but and FSM based model works well IMO.
Louis
31-Aug-2007
[8654x3]
From section 11.12 of the Core users manual:

inp: open/binary/direct ftp://ftp.site.com/big-file.bmp
out: open/binary/new/direct %big-file.bmp
buf-size: 200000
buffer: make binary! buf-size + 2

while [not zero? size: read-io inp buffer buf-size][

    write-io out buffer size  ;<================<<< Where did size come 
    from; this is incorrect, no? 
    total: total + size
    print ["transferred:" total]
]
Whoops!  Watch out for wordwrap.
Sorry. I see it now. My mistake.
DanielSz
31-Aug-2007
[8657]
Exactly, Graham, argument passing in a block is a decent solution. 
I saw here and there rebol code implementing this for similar purposes. 
I'll repost a an updated 'retry version when I get around it.
Louis
31-Aug-2007
[8658]
Attention whoever is in charge of documentation. Section 11.12 of 
the Core Users Manual give an example that will not work. A clear 
buffer statement is needed after the write-io line, and the ports 
need to be closed after sending each file.
DanielSz
31-Aug-2007
[8659x4]
Gregg, do you mean Finite State Machine. Care to expand?
Ok, here is an updated version of 'retry. What is it?  It is a higher 
-level function that takes as input a user function and repeats it 
the number of times specified. The current version handles user functions 
that take arguments.
Usage:

retry :user_function 5 

For a user function that doesn't take arguments

retry/args :user_function 5 [args]

For a user function that take arguments


retry: func [user_function [function!] retries [integer!] /local 
.retry [function!] tries [integer!] value [any-type!] args_length 
[integer!] /args block [block!]] [
	tries: 0
	args_length: length? first :user_function

 if ((args_length > 0) and (not args)) [print ["User function require" 
 args_length "arguments. Provide them in a block and use the /args 
 refinement."] return]
	if not args [args_length: 0 block: []]
	.retry: func [.user_function [function!]] [

  either not error? try [value: do compose [.user_function (copy/part 
  block args_length)]]
			[
			if not unset? value [return value]
			]
			[
			print "Retrying..."
			tries: tries + 1

   if tries > retries [print rejoin ["Tried function " tries " times. 
   Exiting"] return]
			.retry :.user_function
			]
	]
	.retry :user_function
]

Everybody is invited to improve on this.
I wrote 'retry to use in scripts heavy in network calls where failure 
is, by nature of the internet, common, so instead of aborting when 
such a failure occurs, I can use 'retry to repeat individual functions 
any number of times I wish, improving the success of the script.
Gabriele
1-Sep-2007
[8663x2]
>> retry: func [code times] [
[    loop times [
[        attempt [
[            break/return do code
[            ]
[        ]
[    ]
>> retry [1 / probe -1 + random 2] 5
0
1
== 1
DanielSz
1-Sep-2007
[8665x5]
Very elegant, and concise, Gabriele. Maybe you can propise have an 
expanded version to handle arguments the code should be invoked with
Sorry, I pressed Enter by mistake, here I go again: Very elegant, 
and concise, Gabriele. Maybe you can propose an expanded version 
that can handle arguments that the code should be invoked with.
That's what 'retry currently does.
If you provide that capabilty, I take your version in no time. Mine 
is too obscure.
Another requirement is tthat if the code returns a value, retry should 
return that exact same value as well. I believe your version currently 
doesn't.
Ingo
1-Sep-2007
[8670x2]
After reading about /no-set to reduce, is this an error? / inconsistency?

>> reduce ['s: 3 * 3]
== [s 9]
shouldn't it be

== [s: 9]

?
Gabriele
1-Sep-2007
[8672x3]
Daniel, since my version takes a block, you can pass as many args 
as needed.
example: retry [user-function arg1 arg2 arg3 ...] 5
of course, it could be enhanced by reporting the error instead of 
returning none if it fails more than the given times, and so on.
DanielSz
1-Sep-2007
[8675x10]
I was going to apologize to you, Gabriele, a few minutes ago I had 
the sudden realization that your version can indeed handle user-function 
with arguments, as your latest post explains.
Your version really makes rebol shine. Its expressiveness is beyond 
par. Bravo.
Gabriele, just one thing, imagine the following user function: f1: 
func [] [return "f1 body"]
Now: retry f1 4 yields none.
It should return "f1 body", no?
Antoher issue that might be a rebol bug is the use of attempt. Normally 
attempt should return a none  value when an error occurs, but consider 
this.
f2: func [] [ do [1 + "x"]]
>> retry f2 4
** Script Error: Cannot use add on string! value
** Where: f2
** Near: 1 + "x"
That's not what we want.
Again apologies, again my stupidity, all the issues I raised in the 
latest posts are non issues, I made a mistake referencing my functions, 
it should be retry :f1 4, retry :f2 4 (note the colon before the 
function name). and then it behaves as expected. Thanks, Gabriele, 
for this humbling lesson in rebol.
[unknown: 5]
1-Sep-2007
[8685]
shouldn't poke work on port data if pick does?  Seems strange behavior 
for it not to
Gabriele
2-Sep-2007
[8686x2]
i'd actually suggest retry [f1] 4  or   retry [f2] 4   with my code. 
 :f also works but if f takes arguments it would break. so it's cleaner 
to just always use a block.
actually, there is one problem with return, which is if you want 
to do   retry [... return 'something ...]  and have it return from 
the calling function, not retry (like if cond [return 'something]). 
to allow that, you need to add the [throw] func attribute, ie. retry: 
func [[throw] code times] ...
DanielSz
2-Sep-2007
[8688x2]
Thanks for the advice, Gabriele, I'll play around with it for a while...
Gabriele, I'm trying to understand in what kind of situation a throw 
is required. f1: func [] [if true [return "something"]] works without 
a throw, for example. Can you provide a concrete example of code 
that requires the throw attribute to 'retry? Thanks!
Gabriele
3-Sep-2007
[8690x2]
i'm not sure it is needed or not for retry. it depends on how you 
use it.
f: func [] [
    retry [
        ; do something...
        if cond [return something-else]
        ; ...
    ]
    some-val
]