Comments on: Discussion about error handling

Carl Sassenrath, CTO
REBOL Technologies
6-Mar-2010 7:47 GMT

Article #0309
Main page || Index || Prior Article [0308] || Next Article [0310] || 58 Comments || Send feedback

In the REBOL3 group on the REBOL3 world, there's been some discussion about error handling.

Here's a document about it to read and comment on.

Once we've discussed it, we can take it from there and determine what needs to be done to wrap it up.



7-Mar-2010 2:48:31
I am not sure I understood the plan, is it so, that the plan is, to have two types of functions depending on an attribute: -functions with dynamically scoped Return/Exit -functions with definitionally scoped Return/Exit ? (that looks like a reasonable compromise)
7-Mar-2010 2:51:12
Note to the "Should we catch unwinds?" question in the article - the subject of the CureCode #1506 is, that it is (or should be) possible for the interpreter to find out, whether the unwind would cause a "No catch for throw" type error, and, that Try should catch such an "unhandled" unwind.
Brian Hawley
7-Mar-2010 15:01:11
The scoping of return and exit in R3 currently has two problems, both illustrated by the use function:
use: func [
    "Defines words local to a block."
    vars [block! word!] "Local word(s) to the block"
    body [block!] "Block to evaluate"
    apply make closure! reduce [to block! vars copy/deep body] []

The first problem is that the outer function (use itself) will catch any return or exit in the body block because of the dynamic scoping of those functions. In theory, definitional scoping would solve that one.

The second problem is that the inner function (the closure) will also catch any return or exit in the body block, and this would be the case even if those functions were definitionally scoped. To solve this you would need to implement definitional scoping as stated above, then use a function attribute that would tell the closure to not bind return and exit in the body block, so that any return or exit there would return to the function in which the body block was originally defined.

Changing use back to a native (not a bad idea) would not solve the problem because we would still need to write functions like use in application code. So the problem still needs to be solved.

The advantage of definitional scoping over R2's [throw] attribute is that you would be able to use return or exit directly within functions that need to pass through any return or exit in their argument blocks; you can't do that in R2. So I'm in favor of this.

Brian Hawley
7-Mar-2010 15:03:22
However, break, continue, throw, halt and quit all need to still be dynamically scoped. For break, continue and throw the reason is that most of the functions that catch these unwinds don't do any definition at this point, and we don't want them to because of all the bind/copy overhead that would add, and because the dynamic scoping is a major feature that we want to keep (especially for throw).

This means that for break, continue and throw bug#1506 would still need a solution (or to be declared a gotcha and dismissed, but let's ignore that option for the moment). The best solution to that problem suggested so far is to have catch and the loop functions set some task-local flag in the runtime that break, continue and throw can check, and throw an error instead of unwinding if that flag is not set. This would be a little trickier for throw/name, but not much. This would have the advantage of increasing the locality of the unhandled unwind errors, making it easier to track them down. The disadvantage would be increased complexity.

Brian Hawley
7-Mar-2010 15:08:12
Thank you for the explanation of how unwinds work. It mostly matched what I had deduced so far, but the unwinding mechanism is a bit cleaner than I had thought. This will make it easy to rephrase bug#1509 so that it makes more sense, and should be easier to fix. I'll change it so it uses the same terminology.
Brian Hawley
8-Mar-2010 5:36:16
Carl, the "Possible Return Method" section contains two different, mostly unrelated proposals that are mixed in together. It would be best to split the discussion of the return: function attribute into its own section (along with other function attributes, perhaps) separate from the proposal for definitional scoping of return and exit. Then you could add a mention of the function attribute I proposed in the use comment above, which would be a different attribute than return:.
8-Mar-2010 8:07:15
"The second problem is that the inner function (the closure) will also catch any return or exit in the body block, and this would be the case even if those functions were definitionally scoped. To solve this you would need to implement definitional scoping as stated above, then use a function attribute that would tell the closure to not bind return and exit in the body block, so that any return or exit there would return to the function in which the body block was originally defined. " - this is just wrong, as proven long ago, and used in practice, see my Function attributes article
8-Mar-2010 8:10:35
"However, break, continue, throw, halt and quit all need to still be dynamically scoped." - not necessarily, this is a matter of design; the definitionally scoped variants exhibit the same advantages as definitionally scoped Return/Exit, the disadvantage being, that in some cases the cost of binding may be untolerable
8-Mar-2010 9:34:29
Unwinds versus errors

- I am not sure I understand, why there was the need to have both types of exception handling; as far as I know, any of these two types can be used to emulate the behaviour of the other.

8-Mar-2010 9:40:56

I think, that the bugs #771 and #1509 are caused by mixing the "internal" and "external" datatypes - an "internal unwind" (also "triggered unwind" - an unwind *already used* to unwind the execution back to a marker) should not be the same as an "external unwind" (also "untriggered unwind" - a value, which is/can be manipulated by the interpreter as a "Rebol value")

Brian Hawley
8-Mar-2010 15:13:38
"this is just wrong, as proven long ago, and used in practice, see my Function attributes article"

That article was written about dynamically scoped return and exit, and my statement applied to the proposed definitionally scoped versions of those functions. I was giving an example of code that would not work with the definitionally scoped model, and what would need to be done to make that code work.

There was nothing in that message that proposed that return or exit be dynamically scoped at all - it was about exploring the definitionally scoped proposal and its limitations. Your article applied to a completely different system.

"the disadvantage being, that in some cases the cost of binding may be untolerable" (re: the other unwinding functions)

The main disadvantage would be that the functions would not be dynamically scoped. Particularly for throw non-local usage of the function is the primary usage model, and local usage is much more rare (at least in code I've seen). For break and continue local usage is more common, though you still see non-local use. For halt and quit you only see non-local usage; local usage is only seen in test code.

The binding overhead is only a consideration for break and continue; for the others, definitional scoping is not even an option.

8-Mar-2010 17:04:37
"That article was written about dynamically scoped return and exit" - funny, I wrote the aticle just to describe definitionally scoped variants in it...
8-Mar-2010 17:07:30
"I was giving an example of code that would not work with the definitionally scoped model, and what would need to be done to make that code work. " - I have to disagree, having used the definitionally scoped variants for some time now
8-Mar-2010 17:45:07
The nonlocal usage is not an issue for definitionally scoped code, e.g. R1 did not have dynamically scoped constructs, but the documentation contained a section, how to handle nonlocal cases
Brian Hawley
8-Mar-2010 20:11:44
"I wrote the article just to describe definitionally scoped variants in it"

Ah, I had read a different article - link? And as for what I wrote, it was to examine the proposal to change return and exit to be only definitional, not dynamic at all, not even with an option. Does your article deal with that, or does it fall back to dynamic behavior? What is your solution for avoiding the binding of return or exit in the inner function of use with a strictly definitional approach?

"e.g. R1 did not have dynamically scoped constructs"

Ooo, bad comparison. R1 was rewritten for a reason.

9-Mar-2010 4:19:38
Right, there are variants of the FA article (the original site was no longer editable). Anyway, my above question, whether I correctly guessed, that the plan was, to have both types of Return/Exit (i.e. dynamic as well as definitionally scoped), is related to this issue, since in that case (depending on the usage of the word return: in the spec), the USE implementation would just "magically" start to work, so, this may be just a misunderstanding on my side, if the plan is different.
Brian Hawley
9-Mar-2010 5:05:35
That's another problem in the format of the page Carl linked: The use of the function attribute return: wouldn't affect the scoping of return or exit at all. Instead, return: would specify a typespec that the returned value would have to match (according to its proposal), similar to its usage in R2's routine specs. The return: attribute is just mentioned in the wrong section.

We would need another attribute to affect the scoping of return and exit. And that attribute would not have the same meaning as R2's [throw], a workaround which only makes sense with dynamic scoping. With definitional scoping the necessary workaround would have to be something else; it probably shouldn't be called throw: either.

9-Mar-2010 5:20:28
I like the proposal of "return:" as function attribute, actually I've once written a version of func which did just that ...
11-Mar-2010 3:44:55
To document two issues that may be related to this:


    do "while [] []"
is bad when executed in the console, but good when executed in a script:
    attempt [do "while [] []"]

Second: R2 and R3 respond differently here (I am aware of Curecode #666):

        while [return 55][print 1]

Some discussion of these examples here:

11-Mar-2010 4:12:30
Adding my $0.02 to Sunanda's comments: I think, that the do "..." issue is already in CureCode as #851; the WHILE issue in R2 does not apply to the UNTIL function, AFAICT, which I attribute to the fact, that the COND-BLOCK argument of the WHILE function is not treated as it should be, i.e. like the BLOCK argument of UNTIL is.

In R3, the behaviour is most probably caused by #1509.

12-Mar-2010 6:27:26
Check my B-CATCH/B-THROW example illustrating, how a dynamic throw construct can be implemented in a way allowing the "unhandled throw" exceptions to be detected as soon as desired.
12-Mar-2010 6:29:40
The B-CATCH/B-THROW example can be found in my comment to CureCode bug #1506.
12-Mar-2010 7:46:20
Being at it, I would like to know, how many of users use the named Throw?
12-Mar-2010 10:08:41
My guess is, that:

1) named throw is not used by Rebol users

2) it can be safely discarded therefore

12-Mar-2010 10:25:58
A quick search no the Script Library seems to confirm that named throw really is not used (much):

12-Mar-2010 10:33:46
I tend to agree with Ladislav, that named throw seems not to be used much and therefore can and maybe should be safely discarded.

In any case, Ladislav's B-CATCH/B-THROW can easily be extended to handle named catch/throw as well. I've modified Ladislav's code in this regard and also added a few example testcases. The modified B-CATCH/B-THROW is available at

12-Mar-2010 11:19:12
I use it in Cheyenne/Uniserve to create a custom exception in a global user function that should be caught only by Uniserve (while avoiding catching user's exceptions). The function is:
set 'stop-events does [throw/name 'stop-events 'uniserve]

This function's purpose is to break out of WAIT loop when an async scheme works in sync mode.

Brian Hawley
12-Mar-2010 12:35:45
I use named throw as well, for similar reasons to that given by Doc. It's good for writing replacement unwind functions for sandboxing.
12-Mar-2010 14:42:17
To add my $0.02 re named throw usage: I used a similar definitionally scoped construct in BEER, implemented using the unnamed catch function, since I did not like the named throw as offered by the interpreter.
12-Mar-2010 14:52:49
When speaking about sandboxing: currently the named throws are caught by unnamed catch, which I see as a reasonable property (it is posssible to limit the effect of named throws to a specific part code, not allowing it to break out).

What I don't like, on the other hand, is, that when catching a named throw using the unnamed catch, I am not able to rethrow, since I do not know the name.

Brian Hawley
12-Mar-2010 15:17:59
I would like a recover function or catch/recover option which would act similar to the try ... finally operation in many languages with exception handling, but just with unwinds, not errors. (I'll use the name "recover" here for the function, though the name isn't important.)

The recover function would take two code blocks (or catch/recover would take an additional code block). The first would be executed and its return value saved, then the second block would be executed, then the saved value from the first block returned from the function. This might seem like also, but the trick is that unwinds returned from the first block would be propagated as well (by returning them) but the code in the second block should still execute before the unwind is further propagated.

Now it may seem like also does this already, but that is a side effect of bug#1509. Once that is fixed - and it needs to be - also won't be able to do this kind of thing anymore because passing the unwind value as a function argument would be blocked. Only a native function would be able to save the unwind value long enough to execute the second code block, and the code would need to be in blocks so it could be executed without its results passed through a function argument.

In case you are curious, the recover function would be needed for cleanup code that needs to be unwind-transparent. An example of this would be the do intrinsic, which needs to reset the script header reference and change the directory back after running a script file; it currently manages this by using also and bug#1509.

Brian Hawley
12-Mar-2010 15:38:22
Calls to return or exit from code in a string or script don't propagate with the dynamically-scoped model because the do intrinsic could use a [throw] attribute. But they wouldn't work at all from strings with the definitionally-scoped model because all code in strings is non-local code (not bound to the local scope).
>> do does [do "return 1" 2]
== 2  ; should be 1

So, is this a counterargument to definitionally-scoped return and exit or do we declare it a feature? The other unwind functions wouldn't be affected because they would remain dynamic and don't need a [throw] attribute.

Brian Hawley
12-Mar-2010 15:42:01
Ladislav, I don't like that you can catch a named throw from an unnamed catch at all. And I have reported this in CureCode in bug#1518. Please express your support there.
12-Mar-2010 16:12:22
I think that definitionally scoped return/exit give a natural answer to what I'd expect in Brian's example to happen.


>> f: func [a] [do "a * a"]
>> f 42
** Script Error: a has no value

I don't and won't expect above do to automagically bind so that a is accessible. If I'd want that behaviour, I consider it my obligation as caller of do to bind the code according to my expectations beforehand.

I'd therefore also expect the do does [do "return 1" 2] example to result in a "Throw error: Return or exit not in function".

So not only is this not a counterargument against definitionally-scoped return/break, but a rather good argument in favour!

Brian Hawley
12-Mar-2010 18:02:03
Added a few more CureCode tickets related to this discussion: bug#1519 (while conditions), bug#1520 (catch/all) and bug#1521 (recover or catch/recover). Andreas, catch/all would be required to prevent all propagation from string code to regular code, including throw, break and continue, but not halt or quit/now. I wouldn't necessarily suggest this though.
Brian Hawley
16-Mar-2010 17:47:38
Just realized that Ladislav's take on Carl's proposal for changes to the scope model is probably what he meant. So let's analyze it.

Here are the advantages to definitional scoping:

  1. Ease of use for the simple default.
  2. No out-of-scope return or exit (bug#1506 wouldn't apply).
  3. No need for an R2-style [throw] attribute (see above though).

Now given that proposal, here are the related problems:

  1. It's not the default and it's not simple. Using a return: attribute both to specify definitional return and a typespec for the return value would effectively make definitional return a side effect of specifying a typespec. This is unnecessarily confusing. And if definitional and dynamic are both choices, definitional would be the preferred choice of less advanced development (due to bug#1506), the simple choice. And REBOL standard is to make the simplest to use choice the default, even if it is more complex internally. On the plus side, dynamic return is probably faster and most functions don't use return or exit at all, so the (small) extra overhead would be unnecessary.
  2. You would still need to solve bug#1506 for functions with dynamic return, but it might be harder because of how tricky the interactions between the two return models would be.
  3. If dynamic is the default (or even an option) you still need something like R2's [throw] attribute. Definitional solves non-local returns for non-local code, like the outer function of use, but it doesn't solve it for local code, like the code block of its inner function; you would still need an attribute that disables the binding for definitional return (as I mentioned above). They could both be called throw: though.

None of the advantages of the definitional return model work with the proposal as it is. It seems solvable though with some tweaks: Make definitional the default, use a different attribute than return: to choose dynamic return, add a throw: attribute that would disable either catching of dynamic returns or binding of definitional returns, depending, and fix bug#1506 for dynamic returns the same as the other dynamic unwinds.

Or you could simplify things by going all definitional and just adding a throw: attribute that would disable binding of return and exit. No dynamic throw: would be necessary and bug#1506 wouldn't apply. Or you could just stay dynamic, fix bug#1506 and implement the dynamic throw: attribute, R2-style. Either could still have return: for a return value typespec.

17-Mar-2010 3:20:39
Taking into accounts all the issues with Use (problems with 'self, 'return, 'exit) it seems to be advisable to implement Use natively to have a native exception-free context construct in REBOL. In that case, there will be no need to have a third function type - a function using neither dynamic nor definitional Return/Exit, in my opinion.
17-Mar-2010 3:24:04
Re the return: usage in function spec serving two purposes: I like it, and this looks like a good issue for a user poll, to find out, how many users could find it confusing, so, please, express your opinions.
17-Mar-2010 3:29:25
"On the plus side, dynamic return is probably faster" - as far as the speed of the dynamic return is taken into account, there is even a possibility to make the definitional Return faster, since the target is known, as opposed to the dynamic Return, where the target is unknown.

To be fair, I guess, that the simplest possible implementation will be the one not taking any advantages of the nature of the definitional Return and just implement it roughly as fast as the dynamic Return.

17-Mar-2010 3:51:55
About the need to solve #1506 for functions with definitional Return: the solution has already been implemented in R3 - the test of the function context availability.
17-Mar-2010 10:42:32
#1506 for dynamic Return - that has to be solved, but there may be two alternative approaches, either the one I described at the #1506, or something similar as for definitional Return...
Brian Hawley
17-Mar-2010 14:12:51
Changing use back to a native (not a bad idea) would not solve the problem because we would still need to write functions like use in application code. So the problem still needs to be solved. The use function was just used to illustrate the problem; it's not the only reason we need to do this. There are at least a dozen proposed mezzanine functions that we haven't been able to make because of the lack of throw:.

Ladislav, I didn't say that you would need to solve bug#1506 for definitional return, I said that definitional return is itself a solution for bug#1506, at least for return and exit. There is no bug#1506 for definitional return.

However, solving bug#1506 for dynamic return might slow it down enough that definitional return would be faster. This is one of the downsides to keeping dynamic return, even as an option.

17-Mar-2010 15:03:08
Bug#1506 does not necessarily go away with definitional return, it depends on how the return is implemented. For the sake of the following example, assume a definitional return:

fun: does [:return]
ret: fun

This is only solved by definitional return if the return implementation checks for availability of the corresponding function context, as Ladislav mentioned.

17-Mar-2010 18:33:58
"there are at least a dozen..." - just one example (distinct from Use) would suffice to convince me
17-Mar-2010 18:49:17
Actually, the availability of any construct, which would be able to create "any set of words" context together with functions with definitional Return is sufficient to define any Use-like function.
17-Mar-2010 21:38:33
I think with definitionally scoped return we'll be able to write our own [throw]-like function-defining function. Assuming that funco provides definitionally scoped return, it would look somewhat like the following:

tfunc: funco [vars body] [
  funco vars pick [
    [do body] 
    [do bind/copy body first vars]
  ] empty? vars

Which could then be used to straightforwardly define use:

use: funco [vars body] [apply tfunc vars body []]
17-Mar-2010 21:46:34
Posted above example too quickly, so please don't take it literally but only as a general hint at the idea. (For example, tfunc would actually need to be a closure.)
18-Mar-2010 21:44:27
It can be proven, that without a construct allowing you to define "arbitrary" context (with respect to words like 'self, 'return, and 'exit) you cannot do it. On the other hand, with such a construct (e.g. a "native use") it can be done, here is a possible code inspired by your idea:

make object! [
    ; make a function returning b (not general, but works for blocks)
    frb: func [b] [func [] reduce [:b]]
    ; make an "arbitrary" context (implemented here using a "native Use")
    ; works only for non-empty vars blocks
    make-context: func [vars] [use vars reduce [:bind? :quote first vars]]
    ; set words in context to values they have in their current context
    set-in-context: func [words context] [
        foreach word words [set/any in context word get/any word]
    set 'tfunc func [spec body /local vars context] [
        if find spec quote return: [do make error! "return: not allowed in tfunc spec"]
        vars: words-of func spec body
        body: reduce either empty? vars [[:do frb copy/deep body]] [
            context: make-context vars
                :set-in-context vars context
                :do frb bind/copy body context
18-Mar-2010 21:47:50
Errata: the tfunc line should have been:

set 'tfunc func [spec body return: /local vars context] ]

to use the definitional Return

18-Mar-2010 21:57:56
Sorry for putting the long code above, it is incomplete still, I can post a complete version somewhere.
Brian Hawley
21-Mar-2010 23:31:05
I agree, Ladislav, with the solution mentioned in your "construct" messages, but the requirements of such a function need to be more thoroughly specified. The function needs to:
  • Take a block of code.
  • Take a block of words, any valid words including 'self, 'return and 'exit. No words reserved or treated specially.
  • Create a context with the words. You would be allowed you to override the inner hidden 'self field (if one is required to make a context) with a specified 'self field if you like.
  • Initialize the words with either none or possibly a provided value, only. Which of those we decide to do should be decided now, not as a runtime option. I would prefer initializing to none be what we decide to support, rather than requiring an initialization value. Leaving the words unset (like R2's use) is not preferred.
  • bind/copy the code block with the context, without doing the the special treatment of 'self that bind does now, but treating any 'self that is overriden in the block of words like any other word.
  • Return the code block - the calling code can postprocess it as necessary, including calling do.

As Ladislav said, that would probably be sufficient to implement any mezzanine control function like use that needs local words. In theory this could be done most easily as an extension of bind, perhaps by providing a block of words for the context argument, but we would need a name for an option to disable the 'self special treatment: perhaps /no-self.

If we just had bind/no-self then we would still need a way to create a context from a block of words of any word type (not just words or set-words), and no restricted words like 'self, and without needing to make a copy of the block of words in the process (like with collect-words). A separate context creation function would let us initialize the words in the context with set before we bind/copy/no-self the code block, so that could be a plus. We might even be able to do this with a /words option for construct.

The ultimate goal would be to be able to create more functions like foreach and use. We shouldn't be required to make control functions native. Currently we can't even do this kind of thing with extension functions.

22-Mar-2010 5:30:21
An alternative may be, that such a function just takes a block of words and creates a specific context, the word 'self should not be in the context, if not present in the words argument.
22-Mar-2010 8:55:09
I wrote a different version of the Tfunc function based on a "rethrow principle" (looks simpler), and posted it here:
Brian Hawley
22-Mar-2010 14:28:54
Ladislav, "just takes a block of words and creates a specific context, the word 'self should not be in the context, if not present in the words argument" won't work because bind still special-cases 'self, making it visible when it otherwise is hidden. If there is no 'self as the first (hidden, protected) field that would break, and possibly some other internals as well. That is why I suggested the override method.

A (too slow) REBOL function that does what I mean now - ignore the algorithm, look at the effect:

construct-words: funct [words [block!]] [
    obj: make object! length? words
    foreach word words [
        assert/type [word any-word!]
        append obj to-word word

That will handle the 'self override: bind will still special-case the 'self word, but if that word is overriden it will return the overriden value.

>> type? do bind [self] construct-words [a]
== object!
>> type? do bind [self: 1] construct-words [a]
** Script error: cannot set self - it is protected
** Where: do
** Near: do bind [self: 1] construct-words [a]
>> type? do bind [:self] construct-words [a]
== frame! ; This is likely a bug in bind.
>> type? do bind [:self] construct-words [self]
== unset! ; The overriden field is used.
>> type? do bind [self: 1] construct-words [self]
== integer! ; The overriden field is modifiable.

That function won't initialize the values but it doesn't matter because it's a separate function from the binding part. It could easily be done as a native function or as a construct/words option.

You would still need a separate way to bind the code block without the 'self special-casing that bind does, for the case where 'self is not overriden. Hence the bind/no-self suggestion, though better name suggestions are welcome (bind/only is taken).

23-Mar-2010 4:01:44
So, I understand it, that there is currently a design decision:

"Every Rebol context contains 'self"

This is a problem for function contexts, closure contexts, Use contexts, and control function contexts.

While the negative effects of this principle look like being circumvented by the implementations of the respective constructs, the fact is, that they are not circumvented completely, as bugs #1528 and #1529 illustrate.

More hacks can be added to specifically overcome #1528 and #1529, but the general problem remains, since sometimes the users do not expect bind to bind the word 'self to a specific context.

To overcome that limitation, BrianH suggested to introduce the bind/no-self construct above. The problem is, that even that is insufficient, since then, it might be necessary for a user to know, which variant of bind call he should use. While the knowledge of that information is obvious in some cases, it may not be obvious in other cases (if the user obtains an, otherwise "unknown" context e.g.).

The clean solution is to put on equal footing the contexts not containing the word 'self. This makes sense, when we realize, that the function/closure/Use/control function contexts, that do not contain 'self command a majority of contexts in Rebol, so, we can rightly ask, why is it necessary to make it similar to the minority formed by object and module contexts?

24-Mar-2010 18:41:07
(Sorry Brian forced me to post my opinion here) Perhaps the usefulness of having self in FOR loops has been underestimated.

See that counter-example. - How to construct a block of objects from serialized values, in a nutshell.

>> map-each [a b][1 2 3 4 5 6][copy self]
== [make object! [
        a: 1
        b: 2
    ] make object! [
        a: 3
        b: 4
    ] make object! [
        a: 5
        b: 6
Brian Hawley
25-Mar-2010 2:54:32
Steeve, I reposted that in CureCode in the relevant ticket (bug#1529). There are a couple replies, praising the trick but showing that you can do it with bind? just as easily.

Added a third ticket, bug#1543 for bind/no-self (does someone have a better name?). The trick here is that only the user can know whether they want 'self bound; REBOL can't, it can only have default behavior, and options to change that behavior. REBOL can't read your mind (surprise!), it can only do what you tell it to. So if you want it to be able to do something it currently doesn't, you have to add a way to tell it so.

Hey Maxim, would you please write an R3 extension to make it read minds? :)

Maxim Olivier-Adlhoch
31-Mar-2010 12:50:48
comming right up! ;-)


Post a Comment:

You can post a comment here. Keep it on-topic.


Blog id:



 Note: HTML tags allowed for: b i u li ol ul font span div a p br pre tt blockquote

This is a technical blog related to the above topic. We reserve the right to remove comments that are off-topic, irrelevant links, advertisements, spams, personal attacks, politics, religion, etc.

Updated 25-Jun-2024 - Edit - Copyright REBOL Technologies -