Mailing List Archive: 49091 messages
  • Home
  • Script library
  • AltME Archive
  • Mailing list
  • Articles Index
  • Site search
 

Tools for Rebol Coders

 [1/14] from: sunandadh::aol::com at: 6-Jan-2002 4:59


Hi Carl, Carl Read in [REBOL] Re: source code layout question
> No, in that I would think it's probably impossible with REBOL. For > instance, how would you optomize 'blk to allow for a later... > > change blk whatever > > Your "code" from REBOL's point of view is all just data, remember.
True indeed. I was half joking because I know an optimizer would be of similar difficulty to writing a compiler, given that anything can change into anything at anytime. But only half joking. If the other Carl can make his "mental reduction" then there is no reason why a program can't at least make similar suggestions, even if they are wring in a wider context. So my serious suggestion is for lint.r named after the traditional C program that tells you just how many rules you've possibly broken. A Rebol Lint checker could for example highlight possible problems in this code: myFunc: func [Offset Size] [ MyLayout: layout [button "hello"] Offset View/New MyLayout "My Layout" ] -- Function header doesn't specify datatypes -- "Size" defined in function header but not used -- "Mylayout" created as global -- "Offset" in Layout evaluated by not assigned -- "My Layout" string in View/New evaluated by not assigned -- No explicit Return statement Which may immediately lead me to the code I meant to write: MyFunc: func [Offset [Pair!] Size [Pair!] /local MyLayout] [ MyLayout: layout/Size/Offset [button "hello"] Size Offset View/New/Title MyLayout "My Layout" Return True ] Of course, with Lint checkers you have to ignore 90% of what they tell you. But they can spot bugs that the human eye just won't see, so that other 10% can be lifesaver. So I am serious about Rebol having some heavyweight tools: Pretty-print.r with its own dialect for layouting out Rebol code because we will never get the world to agree on a single style. And even if we did, I'd still want my code cleaned up at the end of a messy edit session. Lint.r because an expert eye looking over code is always a useful thing. And it could help newcomers to the language get up to speed. Again it would need its own dialect to say what sort of potential errors you want checked. Andrew's made a start on some of these with the tools he develops in "The Official Guide" Sunanda.

 [2/14] from: joel:neely:fedex at: 6-Jan-2002 8:20


Hi, Sunanda, Minor quibble, and other thoughts... [SunandaDH--aol--com] wrote:
...
> So my serious suggestion is for lint.r named after the > traditional C program that tells you just how many rules
<<quoted lines omitted: 8>>
> -- "Size" defined in function header but not used > -- "Mylayout" created as global
Setting global "MyLayout" (MyLayout might have already existed, rather than being created, and it's entirely possible -- style arguments aside -- to write one or more functions which initialize global data.)
> -- "Offset" in Layout evaluated by not assigned > -- "My Layout" string in View/New evaluated by not assigned > -- No explicit Return statement >
The last one gets closer to my puzzlement over how to write any sort of linting, prettyprinting, etc. for REBOL, given the dynamic nature of the language. I probably won't say this as concisely as I'd like (I don't have time to use fewer words.) In a "fall-through" exit, the value of a function is the value of the last expression evaluated. This is A Good Thing, because I can write: delta: func [a [number!] b [number!]] [ either a < b [b - a] [a - b]] without cluttering up the desired expressions with a bunch of RETURN calls that add nothing to the function. Hmmmm... I think to myself, "Why not a warning if the last expression returns an UNSET value?" For example: printdelta: func [a [number!] b [number!]] [ either a < b [print b - a] [print a - b]] This raises some issues: 1) Maybe I wanted to define a function that is evaluated solely for its (side-)effect and not for a value. This is not uncommon. In languages that give a return type in the function prototype it's easy to distinguish between void foo (...) {...} and int foo (...) {...} to know what the programmer may have intended. We don't have any such hint in REBOL. 2) There are other ways to write the above function in conventional REBOL style, including some that nest expressions quite deeply, as in: printdelta: func [a [number!] b [number!]] [ print either a < b [b - a] [a - b]] The unique "expressional" style of REBOL leads me to think that a lint-like program would have to be capable of full- blown expression (including data type) analysis to be able to determine the type of the last expression (or even to *find* the last expression!) 3) The previous issue becomes even more pronounced when one considers the use of (first-class!) functions as arguments to other functions: foodelta: func [foo [function!] a [number!] b [number!]] [ foo either a < b [b - a] [a - b]] where one must understand something of the behavior of *each* FOO to know whether there's a return-type inconsistency, which implies that different uses of FOODELTA might need to get different warning messages depending on the argument of the moment... 4) REBOL is a dynamic language which only constructs function values by actually evaluating code. Given the ability to construct the blocks that are passed to FUNC, MAKE OBJECT!, etc... one may have no idea from the static text what will happen at run time. The only escape I see from all of this is to have a version of REBOL that requires the result type(s) of functions to be specified in the prototypes as well, and specifies the type signatures of functional arguments (and returned values) also -- a non-trivial change IMHO. -jn- -- ; sub REBOL {}; sub head ($) {@_[0]} REBOL [] # despam: func [e] [replace replace/all e ":" "." "#" "@"] ; sub despam {my ($e) = @_; $e =~ tr/:#/.@/; return "\n$e"} print head reverse despam "moc:xedef#yleen:leoj" ;

 [3/14] from: sunandadh:aol at: 6-Jan-2002 17:37


Hi Joel, Thanks for your thoughtful reply. I'll respond in one lump rather than trying to pick out sentences to quote and comment on. Implicit/explicit Return. This is a matter of style. I tend to always have an explicit Return, to the extent of writing return true to show that I am NOT returning the result of the previous statement. I'd expect a Lint dialect to enable me to turn that rule on, and you to turn it off. That way we both get a usable Lint checker rather than none at all. And yes, it would be difficult to write. And yes it may be derailed by various dynamic changes as a program runs. But who wants to write easy programs!? And I'd suspect most Rebol code would not cause such derailings. Rebol (at least on the web site) present itself as a language for Internet/collaborative programming. But it seems in practice to make life difficult for both those things: -- I write some code that calls something from your site that calls something from someone else's. And at some point it crashes with an "Error near a / b". Is that my code or yours or the other person's or a Rebol-supplied mezzanine function? I don't care how un-purist it is to expect the call stack to be printed. But I need it at this point or the application is dead in the water. -- I think we've all agreed that we find different styles harder to read than others. So if you and I are collaboratively developing code, I'd want to prettyprint yours into a style I prefer, and you'd do the same with mine. This would open doors to collaborative programming. I think there is a philosophical difference between your approach and mine. You are making the case that it is difficult to do something 100%. Maybe that makes you a purist. I'm saying, no worries there, give me the 75% that can be done, and I'll live with the holes, or apply development standards to ensure I don't go near them. I'm not sure what that makes me: a pragmatist or a barbarian? I hope you'll agree that we both have valid cases to make. I hope also that RT are listening to this list and can apply their skills as language designers to solve the problems I see while not damaging the purity we both admire. Sunanda.

 [4/14] from: rotenca:telvia:it at: 7-Jan-2002 1:00


Hi Sunanda,
> Implicit/explicit Return. This is a matter of style. I tend to always have
an
> explicit Return, to the extent of writing > > return true
In Rebol it not always the same thing return or not return: attributes of functions ([] [catch][throw]), for example, change the behaviour with or without return. Not only, look at these 2 examples:
>> a: func[][make error! "p"] >> b: func[][return make error! "p"] >> error? a
** User Error: p ** Near: make error! "p"
>> error? b
== true
>> a: func[][first [p:]] >> b: func[][return first [p:]] >> a
== p:
>> b
== p I think Return must not be used only for style. --- Ciao Romano

 [5/14] from: lmecir:mbox:vol:cz at: 7-Jan-2002 13:46


Hi Romano, <<Romano>> (...) Not only, look at these 2 examples:
>> a: func[][make error! "p"] >> b: func[][return make error! "p"] >> error? a
** User Error: p ** Near: make error! "p"
>> error? b
== true
>> a: func[][first [p:]] >> b: func[][return first [p:]] >> a
== p:
>> b
== p (...) <</Romano>> The first sample: My POV is, that the first sample's function A should behave exactly like the first sample's function B. (it is contained in my REP). If you see it like me, send a request to feedback, please. If you don't, can you tell me your preference? The second sample: Although I am not against a possibility to convert Rebol word types while preserving their binding, I think, that this is a bug (discovered by you). Could you please report it to feedback with a request to correct the behaviour of the function A to yield the identical result as the function B (and with an optional request to supply a binding preserving conversion) ? Ciao Ladislav

 [6/14] from: joel:neely:fedex at: 7-Jan-2002 7:21


Hi, Ladislav and Romano Ladislav Mecir wrote:
> Hi Romano, > <<Romano>>
<<quoted lines omitted: 18>>
> The second sample: > ... I think, that this is a bug (discovered by you).
Based on the documented (RCUG) meaning of RETURN, I agree with Ladislav that both examples demonstrate bugs in REBOL. As you know from the Expressions Chapter, blocks return their last value when they return from evaluation: do [1 + 3 5 + 7] 12 This is also true for functions. The last value is returned as the value of the function: sum: func [a b] [ print a print b a + b ] print sum 123 321 123 321 444 In addition, the return function can be used to stop the evaluation of a function at any point and return a value... The verbiage and examples lead me to conclude that the only difference between explicitly RETURNing a value and implicitly returning the last value is that RETURN may be used to exit prematurely at any point within the function. Nowhere that I can find does the documentation state (nor imply) that f: func [...] [ ... some-expression ] should be any different in value nor effect from f: func [...] [ ... return some-expression ] (particle physics notwithstanding... ;-) -jn- -- ; sub REBOL {}; sub head ($) {@_[0]} REBOL [] # despam: func [e] [replace replace/all e ":" "." "#" "@"] ; sub despam {my ($e) = @_; $e =~ tr/:#/.@/; return "\n$e"} print head reverse despam "moc:xedef#yleen:leoj" ;

 [7/14] from: rotenca:telvia:it at: 8-Jan-2002 1:08


Hi Ladislav and Joel, <Joel>
> Based on the documented (RCUG) meaning of RETURN, I agree > with Ladislav that both examples demonstrate bugs in REBOL.
I think that the set-word return is a bug (already reported to Feedback), but useful to extend to set-word binding preserving conversions. I agree with Ladislav: we need a conversion function for any-words which be binding-trasparent. <Joel>
> The verbiage and examples lead me to conclude that the only > difference between explicitly RETURNing a value and implicitly > returning the last value is that RETURN may be used to exit > "prematurely" at any point within the function. Nowhere that > I can find does the documentation state (nor imply) that
About Return error!, i think it is a documented feature. The error exception happens only when the error! is evaluated. To bypass or delay the evaluation, you must pass it at a function: Return is a function, ergo... The point here is that Return is a function, not an end-before-the-time of block. The difference is also evident when you think at [throw] which throw only Return not every end of block. I could agree with Ladislav when he asks a change in the actual error! specific. But i do not think that the actual implementation of Return error! is a bug. <Ladislav>
>My POV is, that the first sample's function A should behave exactly like the >first sample's function B. (it is contained in my REP). If you see it like >me, send a request to feedback, please. If you don't, can you tell me your >preference?
About Ladislav's Rep, i want to ask him: 0) The by-default-disabled error! is an object! or a error! datatype? 1) When an error is Fired, can it be catched by try? 2) Have you thought about throw err: try [...] ? Don't we need a sort of fire-throw? 3) Normally when an error is throw, it is catchable without try. What happens in your proposal? I think that a big problem about the implementation of error and Return is its interaction with functions attributes [catch] and [] (which throw a return only if is argument is an error!). It is strange, undocumented or buggy (as you like) and all these things constrain to make "strange" things with third of functions to construct trasparent functions (like in Ladislav's transp-func]. --- Ciao Romano

 [8/14] from: lmecir:mbox:vol:cz at: 8-Jan-2002 17:40


Hi Romano, <<Romano>> (...) I could agree with Ladislav when he asks a change in the actual error! specific. But i do not think that the actual implementation of Return error! is a bug. <</Romano>> For me it doesn't matter whether it is a bug or not. I just prefer and propose a more reasonable behaviour. <<Romano>> About Ladislav's Rep, i want to ask him: 0) The by-default-disabled error! is an object! or a error! datatype? 1) When an error is Fired, can it be catched by try? 2) Have you thought about throw err: try [...] ? Don't we need a sort of fire-throw? 3) Normally when an error is throw, it is catchable without try. What happens in your proposal? <</Romano>> 0) I prefer it to be an ERROR! datatype value. 1) I think, that this sample code can illustrate what I mean: fire: func [[catch] error [error!]] [ throw error ] error? try [fire make error! "my-error"] fire make error! "my-error" 2) There are two possible answers to this question. The first one is: there is no need to introduce a new function. Even now the [catch]/throw differs from catch/throw (the former pair works only for errors and "fires" them, the latter works for any Rebol value and doesn't necessarily fire errors). The second one is, that because the mechanisms differ a lot, they should be distinguishable. It is a matter of preference only. 3) I think, that the catch/throw mechanism (which differs from [catch]/throw) should work as it does now. <<Romano>> I think that a big problem about the implementation of error and Return is its interaction with functions attributes [catch] and [] (which throw a return only if is argument is an error!). It is strange, undocumented or buggy (as you like) and all these things constrain to make "strange" things with third of functions to construct trasparent functions (like in Ladislav's transp-func]. <</Romano>> There is a need to have transparent functions in Rebol. (They are useful for new control functions creation.) My implementation is only a hack, because it is a self-modifying code, which is ugly IMHO. The only reason, why it is so ugly, is the behaviour of Rebol errors, which unreasonably complicates things. Cheers Ladislav

 [9/14] from: rotenca:telvia:it at: 8-Jan-2002 18:48


Hi Ladislav,
>> 2) Have you thought about throw err: try [...] ? Don't we need a sort of >> fire-throw?
<<quoted lines omitted: 8>>
> 3) I think, that the catch/throw mechanism (which differs from > [catch]/throw) should work as it does now.
And how to throw and fire an error? If we do in your proposal: throw make error! "my-error" we do not fire our error! and if there is no catch, we end with an ** Throw Error: No catch for throw: "my-error" ** Near: throw make error! "my-error" if there is a catch we have not error at all. instead of (not catched) ** Throw Error: ** User Error: my-error ** Near: throw make error! "my-error" or (catched and evaluated) ** User Error: my-error ** Near: throw make error! "my-error" we must always use the [catch] attribute to fire-throw the error? And if the throw is a script file which some code do, instead of a sub func, what happens?
> There is a need to have transparent functions in Rebol. (They are useful for > new control functions creation.) My implementation is only a hack, because > it is a self-modifying code, which is ugly IMHO. The only reason, why it is > so ugly, is the behaviour of Rebol errors, which unreasonably complicates > things.
I agree, but I think that the function attributes implementation requires a deep revision. --- Ciao Romano

 [10/14] from: lmecir:mbox:vol:cz at: 8-Jan-2002 19:26


Hi Romano, <<Romano>> (...) And how to throw and fire an error? If we do in your proposal: throw make error! "my-error" we do not fire our error! and if there is no catch, we end with an (...) <</Romano>> I thought, that I described it already. If we use the [catch]/throw mechanism, we can fire an error. If we use catch/throw mechanism, we don't fire an error. These two are different things. <<Romano>> we must always use the [catch] attribute to fire-throw the error? <</Romano>> I don't see any disadvantage of that, use the FIRE function I defined, if you prefer. <<Romano>> I agree, but I think that the function attributes implementation requires a deep revision. <</Romano>> Aha, that is where you are coming from. I do not think that the revision is needed. Ciao Ladislav

 [11/14] from: greggirwin:mindspring at: 8-Jan-2002 11:48


Hi Sunanda, et al I didn't jump in on the style thread, but I can include my thoughts here as they are applicable in this context as well. << -- I write some code that calls something from your site that calls something from someone else's. And at some point it crashes with an "Error near a / b". Is that my code or yours or the other person's or a Rebol-supplied mezzanine function? I don't care how un-purist it is to expect the call stack to be printed. But I need it at this point or the application is dead in the water. >> I think this points out that we, as REBOL developers, need to take into account the context in which our code is intended to be used. I can appreciate being able to write simple reblets that are self-contained and often times may get by with a minimum of error handling and formal design. If you are writing code to be used in distributed applications, you darn well better have everything error trapped and be as helpful as possible when it comes to tracking down errors. I think Design by Contract will be enormously helpful in that regard but I haven't had time to work up a dialect implementing it yet. That said, I think call stack information could be very useful in certain cases as well. << -- I think we've all agreed that we find different styles harder to read than others. So if you and I are collaboratively developing code, I'd want to prettyprint yours into a style I prefer, and you'd do the same with mine. This would open doors to collaborative programming. >> I'm not sure about this. My thoughts about style are based on only 6 months of experience with REBOL, and I have yet to find my REBOL "voice". What I *am* finding, is that I use different styles in different contexts. The style I use when writing in the Layout dialect is different than what I use when writing in my FSM dialect. For pure REBOL code, I have some code that is very much block style (style B) but I have other code which is very concise. I think that my goal will be to write code is that easily understood, and easily maintained and I don't know that a single "style" will be the best fit in all cases for REBOL code. For example, if you are writing a novel, you would use a different style than if you were writing an instruction manual or a "How To" booklet. Writing an article for a technical journal would be different still. You may also have a large amount of data mixed in with your code (think Easy-VID, or embedded instructions in an app), what do you do with the data, style wise? How easy something is to maintain doesn't necessarily mean that updates requiring the minimum number of keystrokes are the best. I believe in making the behavior and *intent* as clear and obvious as possible, which doesn't necessarily lock you in to a single style with REBOL nearly as much as with other languages, IMO. If we go down the pretty-printer route, it will dissect a carefully formatted program modeled on the idea of "Literate Programming" and possibly destroy all the structure and meaning contained therein. Yes, that's an extreme example, but I think REBOL will really change the way *I* write programs (it already has) and eventually we'll discover more appropriate tools, which aren't necessarily based on tools we've used for other languages historically. A pretty-printer could still be useful for a certain body of code, to be sure, but there are wide variety of styles in the library and I don't think the style someone used has anything to do with how easily I can decipher the code. The overall organization of a script affects that to a much greater extent. I.e. the more I have to keep in my head, which isn't visible on the screen I'm looking at, the more difficult it is to decipher. Just some thoughts from a baby REBOL. :) --Gregg

 [12/14] from: lmecir:mbox:vol:cz at: 8-Jan-2002 19:50


Hi, <<Romano>> (...) 2) Have you thought about throw err: try [...] ? Don't we need a sort of fire-throw? (...) <</Romano>> I thought about it once again. It looks really as a better idea to distinguish between [catch]/throw and catch/throw. The former should really look different, e.g. like [e-catch]/e-throw as opposed to catch/throw. That could eliminate some nasty interferences, which is A Good Thing. Ciao Ladislav

 [13/14] from: joel:neely:fedex at: 8-Jan-2002 13:32


Hi, Greg, Sunanda, et al... Gregg Irwin wrote:
> > -- I write some code that calls something from your site > > that calls something from someone else's. And at some point
<<quoted lines omitted: 15>>
> regard but I haven't had time to work up a dialect implementing > it yet.
Let's not forget what REBOL alreay provides, which most of us (myself included) probably don't use to full benefit. Pretend that I have some REBOL source files from elsewhere: 8<----"buggyfunc.r"--------------------------------------------- REBOL [] buggyfunc: func [a [integer!] b [integer!]] [a / b] 8<-------------------------------------------------------------- and 8<----"buggywrap.r"--------------------------------------------- REBOL [] buggywrap: func [n [integer!] /local r] [ r: copy [] either even? n [ repeat i n [append r 5040 / i]] [ repeat i n [append r 5040 / (8 - i)]] r ] 8<-------------------------------------------------------------- and now I want to use those bits of scriptage myself. I can write: 8<----"buggytest.r"--------------------------------------------- REBOL [] do %buggyfunc.r do %buggywrap.r testbug: func [n [integer!] /local r] [ either error? r: try [buggywrap n] [ print ["Oops! buggywrap blew up when called with" n "!"]] [ print ["Pretend to do something useful" newline tab mold r]] ] 8<-------------------------------------------------------------- which wraps the "untrusted" code in a TRY evaluation to let me know whether (and under what circumstances) the foreign code may cough and die.
>> do %buggytest.r >> testbug 5
Pretend to do something useful [720 840 1008 1260 1680]
>> testbug 6
Pretend to do something useful [5040 2520 1680 1260 1008 840]
>> testbug 7
Pretend to do something useful [720 840 1008 1260 1680 2520 5040]
>> testbug 8
Pretend to do something useful [5040 2520 1680 1260 1008 840 720 630]
>> testbug 9
Oops! buggywrap blew up when called with 9 ! I suspect that some creative uses of TRY would go a long way toward addressing the issues of making more robust code (even when it uses other, untrusted, foreign components). -jn-

 [14/14] from: greggirwin:mindspring at: 8-Jan-2002 13:04


hi Joel, << I suspect that some creative uses of TRY would go a long way toward addressing the issues of making more robust code (even when it uses other, untrusted, foreign components). >> Absolutely! In addition to TRY, a nice implementation of assertions (i.e. Design by Contract), and tools which make use of REBOL's reflective properites, will certainly help us build larger, more reliable, systems. Undoubtedly, just as with other languages, the tools will grow up around us as the need arises. --Gregg

Notes
  • Quoted lines have been omitted from some messages.
    View the message alone to see the lines that have been omitted