Skip to content

Forgoing faux pas

Christopher Ross-Gill edited this page Aug 1, 2016 · 1 revision

Table of Contents

Some common missteps while learning Rebol

This page may help you avoid some of the idiosyncracies in the Rebol language

If something tripped you up, please add it here. ∗Please start each topic with wikitext ==topic== two equal sign headings.

See • Handy Idioms • for some nice and positive Rebol tips and tricks.




none

Rebol is a value-centric language. This can lead to some little confusion with certain values and certain types. none is one of those special cases.

>> a: none
== none
>> type? a
== none!

So far so good. The source text none when evaluated is the value none and has a type of none!

>> b: [none]
== [none]
>> a: first b
== none
>> type? a
== word!

Huh? This time the source text none hasn't been evaluated (due to the nature of how Rebol handles blocks ... the source has been loaded but not yet evaluated ... a subtle but critical difference) so it's still just a word, with a type of word!. There are few ways around this. All (well almost all) Rebol values have what is called a lexical form. These can be seen with mold/all. These serialized formats are what Rebol detects as the loadable forms.

>> a: none
== none
>> mold/all a
== "#[none]"
>> a: first [none]
== none
>> mold/all a
== "none"

This is a minor point, but the word none (type word!) needs to be evaluated to become the value none (type none!). So if you edit a configuration file with a text editor and put in a none thinking that none is a good default and then LOAD the file, what gets loaded is a word!. This word will test true inside an IF or EITHER expression, even though it looks like none, it isn't; not yet. One way is to evaluate the load with DO LOAD %file, but a safer Rebol way is to use the lexical form, and avoid the DO. Practice safe computing; only DO what you know to be good. The source text #[none] is loaded by Rebol as the value none with a type of none!, no ambiguity involved, for human or computer.

This is true of all Rebol words, values and types; but none can be a little trickier when you are starting out.

Yes and No

There are a few special cases of Rebol source text that do not have equivalent lexical forms. yes and no as well as on and off will mold/all to #[true] and #[false].

false and zero

Just so you know. In Rebol, zero is not false. zero is a value (0) of type integer! and is true. So

>> if 0 [print "true"]
== true

The only expressions that test false in Rebol are false and none, and by that it is the values false (type logic!) and none (type none!). Be careful with unevaluated words false and none as these are of type word! and they will test as true.

The COPY Trap

Rebol does whatever it can to avoid copying data. This saves memory and speeds up the code. In most cases, data is not copied unless you specify it. This can lead to some interesting bugs in your code, if you are not careful when APPENDing, INSERTing or using other operations on a series!.

>> a: [1]
== [1]
>> append a 2
== [1 2]
>> a
== [1 2]

This is of course expected. A simple copy can help you not affect the block you are working on. If instead of directly APPENDing to the block, you can APPEND to a copy of it:

>> append copy a 3
== [1 2 3]
>> a
== [1 2]

No surprises there.

But here's the trap: The trap usually occurs when using a block inside another structure, such as a function:

f: has [t] [t: [] append t 1]

Now if you run this function once, it works OK:

>> f
== [1]

But if you run it multiple times, you get:

>> f
== [1 1]
>> f
== [1 1 1]

We didn't copy the block. Rebol is aggressively reusing the very same memory area for the T block, even if T is local to the function and therefore T just acculumates 1's. To solve this, simply copy the block first. This forces the block to be re-allocated every time you run the function.

>> f: has [t] [t: copy [] append t 1]
>> f
== [1]
>> f
== [1]

So beware whenever using blocks or strings inside structures, such as functions, objects and other blocks.

apply and refinements

Please be aware that when passing arguments to functions with apply, that the refinements are positional.

>> f: func [a b c /one o /two t] [print [a b c one o two t]]
>> apply :f [1 2 3 /two "this is labeled as two" /one "looks like one"]
1 2 3 true this is labeled as two true looks like one

You will see that one prints as true with the data the looks like it was destined for /two. two prints as true and gets the data that looks like it was destined for the /one refinement.

It may be better behaviour to get used to passing the refinements as false for the refinement and with a value of none for the parameter.

>> apply :f [1 2 3]
1 2 3 none none none none

As skipping any optional refinement data may not act as expected under normal interpretation behaviour.

>> apply :f [1 2 3 false true "Data for /two"]
1 2 3 none none true none

You need

>> apply :f [1 2 3 false none true "Data for /two"]
1 2 3 none none true Data for /two

to get what is normally

>> f/two 1 2 3 "Data for /two"
1 2 3 none none true Data for /two
Clone this wiki locally