[REBOL] Re: X.10 serial port control from Rebol
From: greggirwin:mindspring at: 7-Sep-2003 11:42
Hi Parki,
Thanks for posting that. Good stuff! Makes me want to go out and find
an X10 controller so I can play with things again. :)
BP> I have looked
BP> at this in C (working with a developer to port to Mac OS X) as well as
BP> Perl and Java, and can certainly say that the Rebol approach, which
BP> built in serial:// protocol, makes life a snap.
Good to know. We all have different experience, so I always like
hearing where REBOL works well and where it doesn't.
BP> This also contains some playing around with the notions of Singleton
BP> idiom and data hiding. It's likely overkill.
BP> I am posting it here as people might be interested, and I welcome any
BP> feedback on style and whatevernot.
It could be overkill. One of the things about REBOL, or the approach
*I* tend to use with REBOL, as compared to some other languages is
that I don't try to exert too much control over how modules may be
used. That is, REBOL is very flexible, so if I build a module that is
locked down really tight in how it can be used, that makes more work
for me, and more work for the person who wants to use it differently.
It's sort of an accountability thing, and it's a fine line. On one
hand, I don't want to write things so knowledge of internals is
required for people to use it (some VID styles suffer from this IMO),
but on the other hand, in REBOL people can find a way around almost
any blockades you try to put up to protect them.
In this case, I'd weigh the pros and cons of the singleton stuff
versus just using an object. What happens if they create more than
one? Would that ever be useful? Stuff like that.
Now, I can make some comments on your code (already very good) which
are related more to REBOL style and idioms than correct code.
First, your posting line-wrapped heavily here. REBOL is free form, so
you can often adjust the formatting to suit shorter lines if you want.
Second, great comments! Don't forget the COMMENT function for long
comments that may wrap. Also, glad to see you used doc attributes on
your ex function parameters!
Where you use:
repeat count 100 [
You're not using 'count, so you could use LOOP instead.
loop 100 [
Where you use:
if not reply = #{} [ return 0 ]
Consider:
if not empty? reply [ return 0 ]
EMPTY? is often clearer, as to the intent of what you're checking,
than comparing equality against specific values.
Just as FUNC is a shortcut for FUNCTION, DOES and HAS are shortcuts
for functions with no params or those with only local var decls,
respectively. Don't be afraid of one-liner functions.
deinit: func [] [
close serialPort_
]
Could be:
deinit: does [close serialPort_]
Standard REBOL style doesn't use CamelCase for words. It uses all
lowercase, with hyphens, and hyphens are used in place of underscores
in most cases. Also, all-caps aren't used for constants, maybe
because, well, there really *aren't* any constants in REBOL. :)
ex: function [
deviceCode [ number! ] "Device code number: 1,..,16"
funcWord [word!] "The function to run: 'on, 'off, etc"
] [
houseVal deviceVal functionVal addrHex funcHex
...
Vs:
ex: function [
device-code [ number! ] "Device code number: 1,..,16"
func-word [word!] "The function to run: 'on, 'off, etc"
] [
house-val device-val function-val addr-hex func-hex
...
You've made good use of data, but consider how else you might express
your data, and how that would affect the code. e.g.
house-codes: [
#"A" 96 #"B" 224 #"C" 32 #"D" 160 #"E" 16 #"F" 144 #"G" 80 #"H" 208
#"I" 112 #"J" 240 #"K" 48 #"L" 176 #"M" 0 #"N" 128 #"O" 64 #"P" 192
]
...
house-code_: #"A"
...
house-val: house-codes/:house-code_
You could even validate house codes
valid-house-codes: charset [#"A" - #"P"]
; or, with the above house-codes definition
;valid-house-codes: charset extract house-codes 2
valid-house-code?: func [house-code [char!]] [
found? find valid-house-codes house-code
]
You could also use a function for your data conversion lines on the
address and function values. You could even define it locally in EX.
I'm sure you would have done this in your next cut anyway as it's not
really a REBOL specific thing. I do this, sometimes, so I can take
the function and play with it out of context--often in the console.
X10ify: func [val] [
debase/base (to-string copy/part at to-hex val 5 4) 16
;?? will this work here instead?
;at debase/base to-hex val 16 3
]
...
addr-hex: X10ify 1024 + house-val + device-val
func-hex: X10ify 1536 + house-val + function-val
Another handy idiom is to write your function, or a wrapper for it, to
allow multiple values to be iterated over, rather than calling the
function manually with one argument, or arg list, multiple times.
cm11aSend addrHex
cm11aSend #{00}
cm11aSend funcHex
cm11aSend #{00}
Versus:
send-cmds: func [cmds [block!] [
foreach cmd cmds [cm11aSend cmd]
]
send-cmds reduce [addrHex #{00} funcHex #{00}]
If the #{00} command has a special meaning, or is used after every
command, you could give it a special name or just send it silently in
'cm11aSend, maybe based on a refinement. SEND-CMDS could even treat
the block as a simple dialect so you could include WAIT commands and
such.
It's good stuff in any case! I hope my comments give you some thoughts
and ideas. There's certainly nothing wrong with your code as it stands
now.
-- Gregg