8
\$\begingroup\$

I am attempting to break down a Rebol View into a Forth style 'divide and conquer' word set, to optimize readability and organization, and to segregate the business logic from the view layout inner workings.

Business intelligence

Field

  • declaration
  • initialization
  • processing

Software intelligence

  • view layout

Is there a way to simplify the following code?

 REBOL[]
 Result: "Test"
 field1.Process: [
 alert join "You typed: " 
 Result: field1/text
 ]
 field1.Initialize: [do [field1/text: Result]]
 field1.Declare: compose/deep [
 field1: field
 [(field1.Process)]
 (field1.Initialize)
 ]
 block: compose [ 
 (field1.Declare)
 field
 field
 ]
 view layout (block)
  • Layout block structure being

    field: field-type [field-process] [field-initialize]
    

    I seek to optimize notation

    field1.Process: []
    field1.Initialize: [do []]
    field1.Declare: compose/deep [
     field1: field
     [(field1.Process)]
     (field1.Initialize)
     ]
    

    Towards minima

    field1.Process: []
    field1.Initialize: []
    field1.Declare: compose/deep [
     field1: field
     [field1.Process]
     [field1.Initialize]
     ]
    
asked Jun 9, 2014 at 14:44
\$\endgroup\$

3 Answers 3

10
\$\begingroup\$

Try to avoid mixed casing in new dialect designs

Rebol preserves the case of characters when they match the lexical rules to become a symbolic type in the homoiconic structure being produced:

>> to-string quote field1.Process
== "field1.Process"
>> to-string quote field1.process 
== "field1.process"

Yet despite keeping the case in the symbol, it does not use this to distinguish lookups in the (DO "dialect") evaluator:

>> field1.Process: 10
>> print field1.Process
10
>> print field1.process
10

This is why you will rarely see mixed case symbols in Rebol. That is, unless they are in a block representing a dialect that was specially written to have case-sensitive behavior. Your example here is not quarantined in that way. It's all just running in the default evaluator.

Even if it were quarantined in a dialect: not only is lowercase typing consistent with the language's lookup rules, it's easy on the shift key (as is Rebol's choice of square brackets for the primary block delimiter...) Try to stick with it where possible.

Don't fight the lexer, use structure!

When you write something like field1.Process that's going to become a single symbol, you're setting yourself up for further processing work. Now you have to find the position of the period, separate things out, etc. Luckily Rebol makes that easy:

>> parse "field1.Process" [copy left to "." skip copy right to end]
== true
>> print left
field1
>> print right
Process

Still, it's a bit of a shame to not think a little more outside the box. Why not filenames (starting with %) to identify fields, and tags (enclosed in <>) to identify your steps? You don't need to do any extra work:

>> length? [%field1 <Process>]
== 2
>> type? first [%field1 <Process>]
== file!
>> type? second [%field1 <Process>]
== tag!

Depending on your needs, it might be good to free up ordinary words for your dialect keywords. I don't have an immediate intuition for what you are trying to accomplish here. But the point is, the more you can leverage the Rebol typeset the better. And of course there are paths, which are structural:

>> type? quote field1/Process
== path!
>> length? quote field1/Process
== 2

Don't fight the evaluator, make a dialect!

Remember that COMPOSE is (a) something you could have written yourself, and (b) there to help you as a tool in creating your own dialect. You can even use the /NEXT refinement of DO to chain to the Rebol evaluator to execute one full expression and stop:

niftyprint: function [spec [block!]] [
 pos: head spec
 reversed: false
 while [not tail? pos] [
 case [
 #backwards = first pos [
 reversed: true
 pos: next pos
 ]
 #forwards = first pos [
 reversed: false
 pos: next pos
 ]
 true [
 result: do/next pos 'pos
 prin either reversed [
 reverse form result
 ] [
 form result
 ]
 prin space
 ]
 ]
 ]
 prin newline
]

Trying it out, you'll get:

>> niftyprint [{Sum is} #backwards 1020 + 304 {,looc} #forwards "huh??"]
== Sum is 4231 cool, huh??

So you didn't have to reinvent the wheel to delegate to Rebol's inner logic of figuring out (for instance) that infix addition needs to have its two arguments fulfilled. This can be a way of reducing the need to go around putting brackets on everything, and it can become more free.

Of course, that freedom has some drawbacks. You get less checking. But when the tradeoffs are balanced you can get something that has a lot of the feeling of just writing sentences as one would in English.

Moreover, the PARSE dialect itself can be used to make processing your odd blocks all the easier. Consider an alternative implementation of the above:

niftyprint: function [spec [block!]] [
 direction: #forwards
 output: function [value [any-type!]] [
 str: form value
 if direction = #backwards [reverse str]
 prin str
 prin space
 ]
 parse spec [
 (print "hi" probe pos print "there")
 any [
 set direction #backwards
 |
 set direction #forwards
 |
 pos: (output do/next pos 'pos) :pos
 ]
 ]
]

Just a few thoughts to consider.


P.S. As with LISP eventually going with the trends and going by Lisp, REBOL has "modernized" by officially changing to Rebol (Although the originating company, currently inactive after the open-sourcing, is still incorporated as REBOL Technologies.) I personally think it looks better to start scripts with Rebol [], but as mentioned above the lookup is case-insensitive... and mixed case is discouraged. So it's up to you. Kind of a special case.

answered Jun 9, 2014 at 17:06
\$\endgroup\$
1
  • \$\begingroup\$ I hope my editing the question answers your "I don't have an immediate intuition for what you are trying to accomplish here." remark. \$\endgroup\$ Commented Nov 13, 2014 at 20:34
3
\$\begingroup\$

I'm going to simplify your code by removing parts

First attempt:

Reindent and remove possibly redundant field1.:

Rebol[]
Result: "Test"
Process: [
 alert join "You typed: " 
 Result: field1/text
]
Initialize: [
 do [field1/text: Result]
]
Declare: compose/deep [
 field1: field
 [(Process)]
 (Initialize)
]
block: compose [
 (Declare)
 field
 field
]
view layout (block)

Second attempt:

Remove other stuff (this is when I realize you are trying to compose the different bits together. Hmm..):

Rebol[]
Result: "Test"
Process: [
 alert join "You typed: " 
 Result: field1/text
]
Initialize: [
 do [field1/text: Result]
]
block: compose/deep [
 field1: field [(Process)] (Initialize)
 field
 field
]
view layout block

Third:

Essentially, I'll end up with all the code inside block if I keep at it. And that's ok.

Rebol[]
Result: "Test"
Process: [
 alert join "You typed: " Result: field1/text
]
block: compose/deep [
 field1: field (Result) [(Process)]
 field
 field
]
view layout block

Final:

What I think you want, is something similar to this structure:

Rebol[]
config: [
 name1
 "init1"
 [alert join "You typed: " name1/text]
 name2
 "init2"
 [process2]
 name3
 "init3"
 [process3]
 ...
]
block: []
foreach [name init process] config [
 append block reduce [
 to set-word! name
 'field
 to string! init
 to block! process
 ]
]
view layout block

final output of fields

Conclusion

This was a very simple refactoring of your code, and not the only way to do it. Also can be further refactored and improved. Possibly not what you want. The fork offers some advanced tips in this case.

answered Dec 20, 2016 at 23:58
\$\endgroup\$
3
\$\begingroup\$

First, in Last Rebol2/view, you have get-face and set-face accessor functions to set or get the value of a face (there is clear-face and reset-face, use help and source to know more on that).

Advantage is that you don't have to know how the face is implemented to set/or get the value of it (think field, but slider, progress, etc.). And the function does also the screen update if needed (ie, set-face will show the face after setting it, except if /no-show refinment is used).

Second thing is that layout function is able to fetch word values that not belong to the VID dialect. You can even use get-word syntax to enforce it. So you can avoid compose parenthesis overhead in simple cases like this :

Rebol []
Result: "Test"
field1.Process: [alert join "You typed: " Result: get-face field1]
field1.Initialize: [set-face field1 Result]
Below: [alert "see get-word AdvanTage!"]
block: [
 field1: field field1.Process do field1.Initialize
 field :Below ; Rebol is case insensitive, so Below=below
 field
]
view layout block
answered Jan 17, 2017 at 8:58
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.