REBOL DOCUMENTAION suggestions for improvement

Discussion in 'Rebol' started by YueM, Jan 24, 2010.

  1. YueM

    YueM New Member

    in that case try the following:=

    rebol [ ]

    gValue: 35 ; global gValue

    afunc: funct [ ] [ print gValue]


    bfunc: funct [ ] [
    print gValue

    gValue: 433

    print gValue

    ]


    cfunc: func [ ] [
    print [ " 1 - gValue in cfunc " gValue ]
    afunc
    print [ " 2 - gValue in cfunc " gValue ]
    bfunc
    print [ " 3 - gValue in cfunc " gValue ]

    ]


    then call cfunc.

    it gives the following:-

    1 - gValue in cfunc 35

    35

    2 - gValue in cfunc 35

    none
    433

    3 - gValue in cfunc 35

    >>





    the gValue in bfunc behaves like a local value whereas the value in afunc is still global ? why in afunc it is global and in bfunc it is local
  2. Graham

    Graham Developer Staff Member

    Looks good to me ...

    All 'funct does is scan the function block and looks for set words. If it finds them, it creates a local block for them.

    global: 1

    test: funct [][
    set global 2
    ]

    and if you run 'test, it changes the global value.
  3. Pim

    Pim New Member

    More meat on the bones.

    Better Rebol scripts might be encouraged if methods were part of the documentation.
    Does the method for changing the colors of the text-list follow the introduction of the text-list?
    How is a person to know how to change the color of the highlight bar in a text-list?
    The method of changing the highlight color has been posted, but not it seems, in the manual.

    Look at the subject "Arrays" in the "Block Series" Chapter of the Core Manual. Very nice.
    Well written. Good! Bones!!! If it were to fleshed out with a real world method or illustration, it could
    be better.

    Maybe Wikis and HTML pages are a starting point. The Rebol language itself
    could be used to provide a better manual because much of the material published
    on the internet could be brought together by such a script.

    Programming, it seems to me, is both art and science. Individualism and technology. There are
    scripts that are short of applied technology, just as there are scripts - manuals that are short of art.

    Its a head banger if you are a technologist to see someone trash the technology, especially if its
    your technology that's being trashed. At the moment, the library accepts all scripts, highly
    accomplished scrips or scripts that are not that accomplished. This may bother some people...
    scripting in an un-Rebol way....

    Maybe it would be helpful to purge the library every 6 months or so, keeping the most useful
    scripts, vanishing the rest. This would keep the quality up, and make it easier to find good
    information in the library scripts. But purge with a (balanced) committee thanks, not just the
    opinion of one person. The surviving scripts might be worked into or at least associated
    with subjects of the manual.

    I would not be offended to see a script of mine vanish from the library. In fact, I would be happy
    to see that happen to at least a couple of my scripts. But, listening to stuff like "Rebol is not for everyone"
    "...some people program in un-Rebol like way" could almost be offensive. Where is the art?
    Where is the meat?

    Apologies in advance if I don't reply to any replies. Nothing personal to be sure.
  4. Graham

    Graham Developer Staff Member

    I don't know what the REBOL way is, and I'm not too concerned.

    If it gets the job done .. and I don't drive the CPU 100% in getting there, then I'm happy enough.

    Of course it's obvious that REBOL is not for everyone, and I've seen people who just can't get it though they seems to cope fine with other languages. I guess Carl just got too frustrated at seeing so many scripts written for Basic/C/whatever that he vented that day.

    Should we purge the library ? Sounds rather elitist to me. Does that mean we should remove working scripts because they don't meet a committee's idea of purity? Better half a pie than none at all...
  5. henrikmk

    henrikmk New Member

    Bad documentation has been plaguing the rebol.com site for the past ten years, due to how locked down they were and that there was no time to update them. The docs are no longer locked down since the last update to the website to move them to a wiki system, but it takes time to update the documentation.

    I've found that people get the most out of REBOL by asking around (particularly on AltME), as the docs will only reveal so much, but not much on coding technique. Reading Carl's own scripts can be an epiphany. His scripts are simple and easy to understand. He has published a few bits of code, for example the makedoc.r document generator and blogger.r, the blog system he uses on his own website.

    Furthermore, it's important to understand the underpinnings to appreciate the language: How series are managed in memory, how bindings and context work. There are a few people over the years who I've experienced giving up on REBOL, because they attack it from the "surface", i.e. try to simply memorize the function reference, rather than the point and intent of the functions. They will miss what makes REBOL cool. Unfortunately there is little documentation on this other than a few third party articles.

    There is definitely a REBOL way: It's when scripts don't resemble something written in another language and they only take up half the number of lines. :) Beginners often make scripts that resemble something out of PHP or C and gradually as they learn the language underpinnings, the sizes of their scripts shrink.
  6. Sunanda

    Sunanda New Member

    Assuming we are talking about the REBOL.org Script Library.....There is already a community-based method of rating scripts by quality. It just requires a few Library members to take a little time to mark scripts as their favorites and (optionally) to score each favorite from 0-100.

    Sadly few of us have marked any scripts as favorites.

    There is a help page that describes the favorites process in more detail.

    The Popular scripts page takes into account favorites. Favorites scripts will rise to the top of this list.


    All we need is for a few Library members to take the time......
  7. swhite

    swhite Member

    A good point. My scripts look like COBOL.

    But part of the reason for that is that I have to do COBOL-like things. In REBOL, it is easy to read into memory an entire file of data consisting of REBOL data types. But I don't want to do that. I want to read a piece of a file, and that piece is just a fixed-format string of characters that really is several unformatted, but fixed-format, data items--numbers with assumed decimal points, dates in mmddyyyy format, for example. I want to take apart that "record" into its substrings, change the substrings into data types that REBOL can work with, do stuff to them, and then put them back into fixed formats similar to how I first found them. I found a way to do that, but it doesn't look like any REBOL I have seen.

    So, what does that mean?

    Is there a "REBOL way" to do things like that, and how can I find it?

    Should I not use REBOL for things like that? Am I forcing REBOL to work with things it wasn't designed for, in the same way that I would have to force COBOL to work with things that REBOL WAS designed for? Maybe, for some jobs, REBOL is just not an appropriate tool. Simple things are simple, complicated things are possible, but certain specific things are just not worth the effort. Maybe someone should write an article called, "If you want to do this, don't use REBOL."

    Another reason my scripts don't look like anything else I have seen is that I have read Carl's scripts, and I have a lot of trouble following them. Everything is lower case, there is no punctuation, and without having the function dictionary memorized I can't be sure what is a data item or what is a REBOL function. I realize, of course, that those are features and not flaws. I can't understand C either. But "proper" REBOL is so dense that sometimes I can't understand even a one-line script. I have to "un-REBOL-ize" my scripts just to organize my thoughts about how to do things in REBOL. I think it would help a lot with REBOL style if a person could see some of Carl's scripts annotated with a comment-to-code ratio of about ten to one.
  8. Graham

    Graham Developer Staff Member

    Proper Rebol code can be quite dense and unreadable.

    So, it is sometimes better to be verbose and less economical so that the script is more easily maintained.

    Best way to rebolize a script? Post it somewhere for others to comment on.
  9. henrikmk

    henrikmk New Member

    Doing things the REBOL way, simply means taking full advantage of how it works underneath. I think that once you understand how REBOL works underneath, Carl's code becomes much easier to read. I find his scripts extremely simple and clear. It's when I've read his code that I discovered important details about REBOL.

    We have a good example of our friend Ratio, who regularly posts tirades on Carl's blog on how REBOL doesn't behave like a traditional Basic and that most of its features like dialects and datatypes are "useless". Then he proceeds to post code that clearly shows him preferring to work with plain strings, like you would in any other languages, when working with data files, emails, etc. He is particularly adamant that the char! datatype is completely superfluous.

    But it's clear to me, he has not found a way to shift beyond the first gear in his brand new Ferrari. :)

    While you can work that way in REBOL, which proves how flexible the design is, it takes no advantage of words, dialects, datatypes, bindings or contexts. It's slower and requires you to write more elaborate code, string generators, lots of loops, etc.

    REBOL offers a more symbolic approach where you don't need to do any of that stuff and the REBOL way involves taking deep advantage of the data structures that you create:

    1. Copy as little as possible. Instead re-bind when you can.
    2. Use words instead of strings to describe states or values in your code.
    3. Use blocks to store trivial information. There is a small but powerful set of functions to manage blocks and series.
    4. Use objects and dialects to formalize structures.
    5. Use allocation as much as possible to help garbage collection.
    6. Take as much advantage of datatypes as you can. There are about 50 of them.
    7. When returning, either return none! or one other datatype. Those cases are easy to test for.
    8. Building dialects on top of dialects allows for some very powerful scripting.
    9. Many nice things in REBOL are simply side effects of the design.

    I don't expect you to understand these things yet, but these are key elements in the underpinnings of REBOL and you'll gradually experience the advantage in each of these points.

    An example could be point 3. Let's take full advantage of REBOL here, in this perhaps not entirely practical example as it was made on-the-fly and won't really be useful. Ahem. :) But, please bear with me:

    You could keep a very simple list of state machine words:

    Code:
    >> states: [active inactive sleep wake error alarm reset]
    You can keep information about the states in a block of words, and the ordering of the words might determine the order in which states are transitioned to.

    You could be going about your business in the state machine and decide that you end up in state 4 and need to print that:

    Code:
    >> current-state: states/4 ; current-state is wake
    >> print current-state
    == wake
    But instead you could simplify that and just observe the index of the block and dump the first element.

    Code:
    >> states: at head states 4
    == [wake error alarm reset]
    >> print first states
    == wake
    That's fine, but what if you want to save the state to disk?

    Code:
    >> save/all config.txt states
    The index is kept on disk. You don't have to micromanage it. This is nice in case you have a crazy state machine with a varying number of states. :) OK, I'm improvising here.

    If you want to perform an action on each state, each state could be a block of data, which performs something and moves to the next state:

    Code:
    >> active: [do-this set-state 'inactive]
    >> inactive: [do-that set-state 'active]
    etc.

    You need a state switching mechanism:

    Code:
    >> set-state: func [word] [states: find head states word]
    Which means, executing the current state would be to DO the block of data:

    Code:
    >> do first states
    Which means to keep your state machine going indefinitely:

    Code:
    >> forever [do first states print ["state is:" first states]]
    I'm not done yet, because we can take advantage of point 1. You get a situation, where you need to switch between two sets of functions (machines) with the same overall state sequence.

    You could do that by saying:

    Code:
    >> states2: [active2 inactive2 sleep2 wake2 error2 alarm2 reset2]
    And define a new set of functions, but that's crazy and not REBOLish. A better way is to keep your state actions inside a context:

    Code:
    machine1: context [  
      active: [do-this set-state 'inactive]
      inactive: [do-that set-state 'active]
    ]
    Then you can create multiple different machines:

    Code:
    machine2: context [  
      active: [do-this set-state 'inactive]
      inactive: [do-that set-state 'error]
      error: [do-panic set-state 'alarm]
      alarm: [await-button-press set-state 'reset]
      reset: [clean-up set-state 'active]
    ]
    Now you can decide which machine to use with this simple, but extraordinarily powerful line:

    Code:
    >> bind states machine1
    Now the words 'active and 'inactive inside your 'states block are bound to the machine1 context.

    Run it:

    Code:
    >> forever [do first states print ["state is:" first states]]
    or to the machine2 context:

    Code:
    >> bind states machine2
    and run the same forever line again, and it magically runs with machine 2!

    Furthermore:

    - You could proceed to abstract your machine description into a dialect, if your machine becomes very large.
    - You can wrap the whole thing in a context and instance the state machine as many times as you want.
    - You could with a machine dialect, map that dialect to a GUI layout and get a visual layout of the state machine. That's using dialects on top of each other.

    Suddenly getting to the mountain top becomes very easy and all this is thanks to understanding bindings, contexts, dialects and words.

    That's REBOL. Note that the 'states block is less than useful, because it doesn't keep the information needed for the next state. That is stored inside the machine context. But, again, not a practical example.

    Sorry for the long post.
  10. Gerard

    Gerard New Member

    YueM,

    Graham is right. Thanks for the quick explanation Graham.

    I also asked myself for a long time how 'funct was working and since I didn't find a way to get at it
    I decided not to use it because I could not see how it really worked and never verified if it was a native or a mezzanine.

    Now with the hint Graham offered I Looked at the funct content using source funct to see if I could confirm the fact that 'funct is really scanning for set-word! in the body block. It really does using some parse rules.

    Even if you got it from Graham's succint explanation, I prefer to explain it in greater details for other newcomers that would not be able to understand 'parse rules at this stage.

    In fact if 'funct finds some word names followed by a semi-colon (like Gvalue: ) it really creates some local word using the same name.

    But when no set-word! is found, 'funct simply uses any similar 'word already defined in the global context (as defined in system/words) and use its value, if any. In the case the 'word is unset! (eg if you used Gvalue2 instead of Gvalue in 'afunc),
    you would have got an error msg, stating that this 'word has not been assigned any value yet.

    That's why 'afunc returned 35 and not none as is done by 'bfunc, since in 'bfunc you previously assigned a new initial value to the now local Gvalue.

    In fact if I remember well what goes on with almost every language, if we don't assign an initial value for a local variable, it is left unknown - this being a source of many programming errors.

    Here REBOL offers what can be considered by many as a feature, using any global 'word already defined that uses the same identifier.

    So the RULE is : if you use 'funct, be sure to initialize any new local 'word you defined to some really wanted value,
    if you don't want REBOL to use another GLOBAL one instead.

    HTH
  11. endo

    endo New Member

    Are you kidding? :) Your post is a great example for beginners about how to think in rebol way.
  12. Gerard

    Gerard New Member

    Henrik,

    Thanks for helping us to illustrate with simple, concise and useful examples some of the remaining concepts that need more explanations before they be well understood.

    For convenience I restate below your list of REBOL concepts and tools to learn and eventually master, as I will comment and use it as my starting point :

    First your simplified and partially implemented Finite State Machine example is fine since it precisely puts in evidence the first 4 items from your list and is kept minimal in regard to the other concepts needed to understand your script.

    This is often not well understood by other script submitters or they simply don't want to put all the efforts and time required to do it right. But it's OK too because it is not their job to teach others ...

    Coming back to my main point, you did a fine job illustrating in a single minimal script how to use the 'bind word, the block data structure combined with using words instead of string, when appropriate to do so.

    Furthermore you also showed how to efficiently restrict the number of global words and useless re-copy using
    'context to restrict the scope of the defined words inside an object! (since 'context is a shortcut for 'make object!) and
    'bind for creating links between items inside 'states and the ones found inside 'machine1.

    Of interest here to newcomers:
    ===================

    I almost missed the point about 'bind since at first I thought that bind states machine1 had the same effect that the operation which consists of setting 'states to the machine1 object value (eg set 'states machine1).

    Then I tried to get at the internals of each one and saw I was completely lost.

    After some probing what comes in here is this : every word contained in 'states (eg 'active and 'inactive) is linked to the value associated with his counterpart (same 'word) found in the machine1 object. Kind of implicit foreach is applied on each word...

    I presumed it was done using an internal copy operation, but after some modification on each side I realized that when I modify the machine1/active contents and re-test the contents of 'states with get first states REBOL simply returns the newly modified contents and vice-versa when I first modify 'states in first place. But the words from machine1 and the ones from states both point to the same contents.

    So I suspect REBOL instead of using the 'copy operation uses the same kind of reference (probably an indirect pointer in other languages) as when we use something like this : word2: word1: copy [ ] instead of using word2: copy word1: copy [ ]
    so that further actions to 'word2 also affects 'word1.

    ; ------------------------------------------------------------------------------------------------
    ; -- Here is a part of my experiments
    ; ------------------------------------------------------------------------------------------------

    >> states: [active inactive]
    == [active inactive]

    >> machine1: context [
    [ active: [do-this set-state 'inactive]
    [ inactive: [do-that set-state 'active]
    [ ]

    >> bind states machine1
    == [active inactive]

    >> get first states
    == [do-this set-state 'inactive]

    >> get second states
    == [do-that set-state 'active]

    >> set first states [done]
    == [done]

    >> probe machine1
    make object! [
    active: [done]
    inactive: [do-that set-state 'active]
    ]

    >> machine1/inactive: [again]
    == [again]

    >> get second states
    == [again]

    ; -------------------------------------------------------------------------------------------------------
    ; -- This is definitely different from the following --
    ; -- Now a real object is linked with 'states2 and it is not an indirect reference to
    ; -- its internal parts as was the case for 'states
    ; -------------------------------------------------------------------------------------------------------

    >> set 'states2 machine1

    >> probe states2
    make object! [
    active: [done]
    inactive: [again]
    ]

    >> get first states2
    ** Script Error: get expected word argument of type: any-word object none
    ** Where: halt-view
    ** Near: get first states2

    >> states2/active
    == [done]

    >> get first states
    == [done]


    So with 'bind both words contained inside 'states [eg 'active and 'inactive] which were previously unset! are now associated with [do-this set-state 'inactive] and [do-that set-state 'active] respectively, as if specific set-words had been used (eg set first states [do-this set-state 'inactive] and set second states [do-that set-state 'active] ).

    A future extension planned for this 'bind experiment
    ================================

    In a near future I plan to test this 'bind operation just to see if I can efficiently implement some kind of Class-level method to be shared by many anonymous REBOL objects contained in a list. Actually there seems to be available only Instance-level method, i.e. only a copy of a common method can't be shared from the inside of objects themselves. They have to be copied inside each new object created. May be some dynamic link stored externally in another context could be used instead to store the Class-level method to be shared ...

    About memory usage
    =============

    I also often asked for myself: "Is there some easy way to compare the internals effects about the memory used when any new datatype value is allocated in memory or later modified (extended, removed or cleared) ? "

    I had a similar need for solving some issues from my own experiments and the only way I found to inspect the memory used by REBOL was the system/stats, which gives me only a global use of memory before and after some operation. Not a lot useful !

    Yesterday I searched for other memory related scripts from the REBOL.ORG library and found other scripts but didn't have time to explore them yet (%mem2.r, etc...). Will come back with more about this eventually.

    This memory usage aspect is left unexplained in the official documentation and probably the only persons who could get these tools running are Gurus that talked about this theme with Carl in the past... (Romano the author of %mem2.r is for sure one of them).

    This aspect of REBOL (link between data structures and the real memory usage) is missing too from the doc, from my POV, but may be it's simply my knowledge of REBOL and its relation to the machine that is not deep enough ...

    Follow-up
    ======

    What would then be needed as a follow-up to this post is some projects proposals to enhance similar small starting scripts and some feedback offered by others - a bit like is done in the Mailing List but organised from the start.

    This is what I plan to pursue with my own project (I will come back soon about it on REBOL.ORG).

    Hope others will submit some too. Sometimes trials like YueM did with his script for comparing scope of global vs local words are sufficient to start some discussion and clarify concepts.

    ML always did it right in the past. Now AltMe and R3 Chat do a similar job but they are more closed to the world.

    That's enough for this post. The remainder of my reflection about doc enhancing will follow shortly in a subsequent post.
  13. YueM

    YueM New Member

    thanks Henrik for the state machine example. I am not at that level where I understand fully the bind and context concepts yet. I am still digesting your examples.

    Gerard, thanks for the clarifications , now it's clear to me that inside funct , as soon as you assign a new value to a variable, with the same name as a global variable declared outsiide of funct , the variable becomes local to funct.

    It's also good you are kinda digging further into comments made by Graham and Henrik. It gives me more insight into those things.

    In that case I think I will stick with func [ /local ] form which makes it very clear when someone is reading the code, that a variable is local, and anything else is global. this can be seen right away by just looking at your func header. it shows the benefit of strongly typed languages, where everyone must declare one's variable before one uses it, and is a lot easier to read.

    If your program is small funct is ok, however when you have to design a big program, it's too easy to miss whether a variable is local or global, without going through the funct code line by line to see if a value has been assigned to a variable.

    yes I know I can use my own style to declare and initialize every local variable in funct , however when I work in a large team of developers, I cannot force everyone to adopt my style, and to make modifications to a program done by someone who has his own style might pose some problem.

    I was experimenting with vid and I am running into some difficulty there with panels. I am going to open a vid thread to post the example.

    yes it would be good if other people bring in their sample scripts for discussion on this forum. the discussion would benefit other beginners like myself.
  14. endo

    endo New Member

    Hi, you may misunderstood the difference between func /local and funct.
    Check the exmaple below,

    >> f: funct [] [a: 3]
    >> a
    ** Script error: a has no value

    >> f
    == 3

    >> a
    ** Script error: a has no value (a is still undefined in global context, because of funct)

    >> ff: func [] [b: 5]
    >> b
    ** Script error: b has no value

    >> ff
    == 5

    >> b
    == 5 (ff function sets the b to 5 in global context, because we did not use /local b)

    So actually it is better to use funct in a big project, so no one can create or change a value of a global variable inside a function accidently.
  15. Gerard

    Gerard New Member

    FYI,

    Here's a REBOL collection of tips and tricks collected over the years by Brett Handley from many contributors.

    These help clarify many undocumented or misunderstood REBOL features ...
    (objects, contexts, printer use, math operations on binary values, global namespace limitations, keyboard polling techniques, parse use, ...)

    http://www.codeconscious.com/rebol/tips-and-techniques.html

Share This Page