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]
        ]
    
\$\endgroup\$
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.

| improve this answer | |
\$\endgroup\$
  • \$\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\$ – OneArb Nov 13 '14 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.

| improve this answer | |
\$\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
| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.