[REBOL] Re: [ A world of words ] naming convention, namespace, namespace poll
From: greggirwin::mindspring::com at: 4-Jun-2003 14:49
Hi Marc,
MM> Unfortunately there is a small mistyping (the ":")
Thanks for catching that! I'm not always as careful as I should be
when responding to ML items; I go too fast and think too little. :)
Thanks also for your detailed response. Now I can see clearly the
issue we're up against, with the USE context being shared between
instances. I thought about it for a little bit, and didn't come up
with any brilliant solutions. While it might be possible to cook one
up, it would likely obscure the main point and cause confusion if used
as a teaching tool.
So, I started on my real work for the day, but it nagged me, and then
an idea began to grow in my mind. Given that REBOLs approach to OO
concepts is different from the norm, should we really try to force fit
standard examples into it? Maybe not. If students need to learn Java,
C++, or .NET rules about how OO works in those enivirons, how valuable
is it to see examples in REBOL. In other words, the OO concepts can be
pure, but the implementations are not. In that light, how valuable is
it to be able to see those concepts in action? I place a high value on
that kind of thing myself, but there is a mismatch. If you create an
articfical environment for demonstrating OO concepts, it doesn't
really matter how it works behind the scenes, or if the language used
is useful for other things (i.e. in the real world) versus being used
*only* to explain and demonstrate OO concepts. What I'm talking about,
of course, is building a dialect in REBOL which would be used to
explain and demonstrate OO concepts as clearly as possible, without
any particular regard for practicality.
But I digress...
MM> ;;;;;;;;;;;;; WHAT I WANT... THE "TANK" EXAMPLE
Rather than taking a traditional approach which, as I said, would
probably end up being less than attractive, I decided to look at it in
a different way; a REBOLish way.
What we want, is to use abstraction and information hiding to our
advantage. Where REBOL differs is how strict it is about enforcing
some of these things. In your scenario of the horrible code we *may*
write, REBOL doesn't offer us much protection in how it implements OOP
concepts. So how do we elegantly provide encapsulation and
abstraction? How about with a dialect?
Using your comments as a guide, I put together a dialect based
approach to the tank problem. I started out simple, but kept getting
ideas and decided to play with them a bit. (code at end of message)
This approach doesn't solve the problem of completely preventing
people from doing bad things if they have access to the code and data
structures used internally, but it does show that you can provide a
public interface that only allows certain things to be done.
The way results are returned is...different. As I worked, I wanted a
way to see the results of a command in context, then I started
thinking about messaging in a remote or asynchronus enivronment, and
*real* messaging as opposed to just RPC. I'll have to play with the
idea more, and think about how the grammar needs to be bi-directional;
that is, the client side may want to parse the response it gets.
In any case, let me know what you think. I have to go do some real
work now! :\
-- Gregg
; Code below. Watch for wrap!
REBOL []
tank: make object! [content: 0 capacity: 100]
tank-context: make object! [
tanks: copy []
results: copy []
; This FORMs all results, so numeric result may not want to use it.
add-result: func [item] [append results reduce [reform item]]
; Dialect Rules
rules: context [
available:
none
; Parsed data (using a trailing * to denote them)
id*: amount*: word*:
none
gallons: ['gallons | 'gallon]
tank-id: [
opt 'tank set id* [issue! | integer!] (id*: to issue! id*)
]
make-tank: [
'make tank-id opt [
opt ['with 'a] 'capacity opt 'of
set amount* integer! opt gallons
]
(
either find/skip tanks id* 2 [
add-result ["tank" id* "already exists"]
][
append tanks reduce [
id* make tank compose [
capacity: (either amount* [amount*][100])
]
]
add-result ["tank" id* "added"]
]
)
]
destroy-tank: [
'destroy tank-id
(
either find/skip tanks id* 2 [
remove/part find tanks id* 2
add-result ["tank" id* "destroyed"]
][
add-result [
"tank" id* "didn't exist, so was not destroyed"
]
]
)
]
add: [
'add set amount* integer! opt gallons ['in | 'to] tank-id
(
available: tanks/:id*/capacity - tanks/:id*/content
tanks/:id*/content: tanks/:id*/content + min amount* available
add-result [
"Tank" id* "now contains" tanks/:id*/content "gallons"
]
)
]
subtract: [
; 'remove might be a better word than 'subtract
'subtract set amount* integer! opt gallons 'from tank-id
(
available: tanks/:id*/content
tanks/:id*/content: tanks/:id*/content - min amount* available
add-result [
"Tank" id* "now contains" tanks/:id*/content "gallons"
]
)
]
get-value: [
opt 'get tank-id set word* word!
;(add-result tanks/:id*/:word*)
(
add-result switch/default word* [
content [[
"tank" id* "contained" tanks/:id*/:word* "gallons"
"at" now/precise
]]
capacity [[
"tank" id* "can contain a maximum of"
tanks/:id*/:word* "gallons"
]]
][
["Tank" id* "doesn't have a" mold form word* "value"]
]
)
]
main: [
some [
make-tank | destroy-tank | add | subtract | get-value
]
to end
]
] ; End of dialect rules
set 'tank-manager func [command [block!] /notify fn [function!]] [
clear results
if not parse command rules/main [
add-result ["command" mold command "failed"]
]
if notify [fn results]
results
]
]
cbk-fn: func [
"Callback function for tank-manager."
results
] [
print mold results
]
tc: :tank-context ; just a lazy shortcut for console fiddling
tank-cmd: func [command [block!]] [
tank-manager/notify command :cbk-fn
]
tank-cmd [make tank #1]
tank-cmd [make tank #2]
tank-cmd [make tank #3 capacity 200]
tank-cmd [make tank 4 with a capacity of 500 gallons]
tank-cmd [make #4 with a capacity of 500 gallons]
tank-cmd [make #5 capacity of 500 gallons]
print ""
tank-cmd [destroy tank 4]
tank-cmd [destroy tank #4]
tank-cmd [desotry tank #5]
tank-cmd [destroy tank 5]
tank-cmd [destroy tank #3]
print ""
tank-cmd [add 50 gallons to tank 1]
tank-cmd [add 80 in tank 2]
tank-cmd [subtract 20 from tank 2]
tank-cmd [add 999 to #1]
tank-cmd [subtract 5 gallons from tank 1]
tank-cmd [ ; two commands at once
subtract 5 gallons from tank 1
add 25 gallons to #2
]
;tank-cmd [subtract 105 gallons from tank 1]
print ""
tank-cmd [#1 content]
tank-cmd [get #2 capacity tank 2 content] ; two commands at once
tank-cmd [get #1 material]