[REBOL] [enum] Re: Some other questions
From: joel::neely::fedex::com at: 22-Nov-2003 8:48
Hi, again, Mike,
Mike Loolard wrote:
> 2) How do you create custom types ?
> I am talking of a way to emulate the typedef behavour in C ?
> How would you go to create an "enum" type in REBOL ?
>
Strictly speaking, there are two answers to your first question:
1) You can't. But REBOL has a very rich set of types already,
so you can often find a type that suits your purpose (if you
ask yourself the right questions!)
2) You can create objects, and use those as prototypes to creat
other objects.
There's a real problem is with your second question (and if you
don't read anything else I write, PLEASE remember this one!)
COBOL is alphabet blocks.
C is a set of tinkertoys.
Java is an Erector Set (IIRC similar to Meccano in Europe?)
with all kinds of braces, motors, girders, nuts, and bolts.
Perl is a Swiss-army chainsaw.
REBOL is modeling clay.
Learn to use each one according to its own nature.
Let's talk about question 3 and then return to the first one. As
I said above, we must first ask ourselves the right questionS. If
your question is really
How can I create a set of names for distinct, related values
to use in my REBOL program?
Then the answer is easy: words!
Suppose I am doing something with personnel data, and want to
represent the status of each employee. I could do something like
the following:
employee: make object! [
ID: 0
name: ""
hire: now/date
status: 'full-time
]
Notice that the STATUS attribute is a word! value, whose name is
chosen for mnemonic value. That's all there is to it. Now let's
proceed to "hire" some people, with:
bob: make employee [
ID: 1234
name: "Robert R. Robertson"
hire: 01-Jan-1997
]
carol: make employee [
ID: 2345
name: "Carolyn C. Carroll"
hire: 14-Feb-1998
]
ted: make employee [
ID: 3456
name: "Theodore T. Theoden"
hire: 27-Nov-1999
status: 'part-time
]
alice: make employee [
ID: 4567
name: "Alicia A. Allison"
hire: 31-Dec-2000
status: 'leave
]
Notice that BOB and CAROL are created with the default status of
full time, while TED and ALICE each have different status. Here
we had to "quote" the words, making them LIT-WORD! values, to keep
REBOL from trying to evaluate them. We don't care about whether
there's a value associate with e.g. LEAVE , but simply want to use
the word itself as a label.
Let's use a block as our pretend database:
all-employees: reduce [bob carol ted alice]
The reason we had to REDUCE that block is that here we don't want a
block of words, but want a block containing the objects that those
words are set to. (We could also have written a "constructor"
function which would make a new employee from the arguments passed
to it, and would append that new employee to ALL-EMPLOYEES at the
same time.)
Then we can produce an employee report, grouped by status with the
following (rather inefficient!) function:
group-report: func [
/local count
][
foreach [
status title
][
full-time "Full Time (at least 35 hrs/wk)"
part-time "Part Time (up to 34 hrs/wk)"
leave "Leave of Absence (half benefits)"
suspended "Disciplinary Suspension (no benefits)"
terminated "Not Here (no further information available)"
][
count: 0
print [title newline]
foreach emp all-employees [
if emp/status = status [
print [tab emp/hire tab emp/ID tab emp/name]
count: count + 1
]
]
print [newline count "employees in this status" newline]
]
]
When we evaluate that function, we get:
>> group-report
Full Time (at least 35 hrs/wk)
1-Jan-1997 1234 Robert R. Robertson
14-Feb-1998 2345 Carolyn C. Carroll
2 employees in this status
Part Time (up to 34 hrs/wk)
27-Nov-1999 3456 Theodore T. Theoden
1 employees in this status
Leave of Absence (half benefits)
31-Dec-2000 4567 Alicia A. Allison
1 employees in this status
Disciplinary Suspension (no benefits)
0 employees in this status
Not Here (no further information available)
0 employees in this status
Notice that the second argument to the outer FOREACH in the function
above is a block containing alternating WORD! and STRING! values: the
word is the name/symbol of a status, and the string is the associated
description. (In a real application, this would be global to the
entire application, of course!) Since we didn't REDUCE that block,
we literally have words and strings.
The REBOL word type can be used in this way -- as a symbol with no
associated value -- in the same way that one might use atoms in LISP,
scalar types in Pascal, or (sort of) enum symbols in C. But remember
that C enum types actually have associated values, which is *not* the
case with REBOL words used as above.
However, (and finally, for this sub-question), we could have begun
by defining our status titles *as* the values of a set of words, and
then just used the names of the strings:
full-time: "Full Time (at least 35 hrs/wk)"
part-time: "Part Time (up to 34 hrs/wk)"
leave: "Leave of Absence (half benefits)"
suspended: "Disciplinary Suspension (no benefits)"
terminated: "Not Here (no further information available)"
when we subsequently created our employees:
employee: make object! [
ID: 0
name: ""
hire: now/date
status: full-time
]
; and so on, through...
alice: make employee [
ID: 4567
name: "Alicia A. Allison"
hire: 31-Dec-2000
status: leave
]
All employees created with a status of FULL-TIME will have the title
string in their object, but it is *a*reference*to*the*same* string,
not a copy of the string, so there's no wastage of memory. There are
certainly some pros-and-cons here, but I wanted to point out the
reference-vs-copy issue, as it can lead to significant differences in
how you can use strings in REBOL vs some other languages.
FINALLY (whew!) to return to your first question: You can't create
your own types, so even if you use objects or words to implement your
application's concepts, there's no way (aside from writing your own
run-time
checks) to enforce the rules about those conventions. A
function to perform some operation on an employee would still have to
be written something like
flibbertigibbet: func [emp [object!]] [...]
which would pass the automatic argument type tests if *ANY* object were
passed, not just one of your employee objects. Likewise, you can write
a function which requires a word as one of its arguments, but there's
no way to require that it be one of a particular set of words (e.g. the
status of an employee) without including in the function your own
checking
status-words: [full-time part-time leave suspended terminated]
;...
somefunction: func [blahblah [word!] ...] [
either find status-words blahblah [
;... proceed normally
][
;... generate your own error explicitly
]
]
Hope this helps!
-jn-