• Home
  • Script library
  • AltME Archive
  • Mailing list
  • Articles Index
  • Site search
 

AltME groups: search

Help · search scripts · search articles · search mailing list

results summary

worldhits
r4wp17
r3wp20
total:37

results window for this page: [start: 1 end: 37]

world-name: r4wp

Group: Rebol School ... REBOL School [web-public]
Steeve:
3-Oct-2012
I don't think you need 'with  at all, you can throw back the new 
parameters as an argument of the throw function, like:
>> throw/name reduce args 'recurse
Steeve:
3-Oct-2012
Here is a version with no locals, no temporary context, no shit and 
not tested ;-)

rfunc: func [spec body][
	func spec compose/deep [
		forever [
		   set [(spec)] catch/name [
			  return (func spec body) (spec)
		   ] 'recur
		]
	]
]
recur: func [args][throw/name reduce args 'recur]
Ladislav:
3-Oct-2012
OK, this is the long version:

tail-func: func [
    {

  Define a recursive user function with the supplied SPEC and BODY.
     	The function can use a special TAIL-CALL local function
     	to perform a tail-recursive function call.
    }
    [catch]


 spec [block!] {Help string (opt) followed by arg words (and opt type 
 and string)}
    body [block!] {The body block of the function}
    /local the-function tail-call context-word
] [
	; define a new 'tail-call local variable
	tail-call: use [tail-call] ['tail-call]
	
	; bind the given BODY to "know" the 'tail-call variable
	body: bind/copy body tail-call
	
	; find a local word in SPEC
	context-word: find spec word!
	if context-word [context-word: first context-word]
	
	; define the TAIL-CALL function
	set tail-call func spec compose [
		(
			either context-word [
				; set parameters to the new arguments
				compose [set parameters values? (context-word)]
			] [[]]
		)
		throw/name none 'tail-call
	]
	
	; define the function
	the-function: throw-on-error [
		func spec compose/deep [
			(either context-word [context-word] [[]])
			while [true] [
				catch/name [
					return do [(body)]
				] 'tail-call
			]
		]
	]
	
	if context-word [
		; get the function context
		context-word: bind? first second :the-function
		
		; replace the context word in the function body by NONE
		change second :the-function none

		; adjust the TAIL-CALL body
		; replace the 'parameters word

  change/only at second get tail-call 2 bind first context-word context-word
	]

    :the-function
]

values?: func ['word] [second bind? word]
Steeve:
3-Oct-2012
I think I included all your modifications Ladislav but shortly :-)

rfunc: [spec body /local args][
	args: to-block form first (

  do second func spec compose [bind? (to-lit-word first find spec word!)]
	)
	funct spec compose/deep [
		recur: func spec [
				throw/name reduce [(args)] 'recur
		]
		forever [
		   set [(args)] catch/name [
			  return do [(body)]
		   ] 'recur
		]
	]
]
Steeve:
3-Oct-2012
Should do the trick:

rfunc: [spec body /local args][
	args: to-block form first do second 
		func spec compose [bind? (to-lit-word first find spec word!)]
	funct spec compose/deep [

  recur: quote (func spec compose/deep [throw/name reduce [(args)] 
  'recur])
		forever [
		   set [(args)] catch/name [return do [(body)]] 'recur
		]
	]
]
Steeve:
4-Oct-2012
Last version.

- Any spec accepted but needs at least one parameter (can be just 
a local)

rfunc: func [
    [catch]
    spec [block!] body [block!] /local arg obj recur
][
    throw-on-error [

        if error? try [arg: to-lit-word first find spec any-word!][
            make error! "rfunc needs at least one parameter."
        ]
        recur: func spec compose [throw/name bind? (:arg) 'recur]
        obj: catch/name [do second :recur] 'recur
        funct spec compose/deep [
            recur: quote (:recur)
            forever [

                set/any [(to-block form first obj)] second catch/name [
                    return do [(body)]
                ] 'recur
            ]
        ]
    ]
]
Steeve:
4-Oct-2012
Not really fair, you redefine the throw word and expect it to work 
as is ?

Your function got the same problem on my pc, except it occurs at 
the execution time.
** Script Error: Cannot use path on integer! value
** Where: tail-call
** Near: throw/name none 'tail-cal
or maybe you have a new version right now

(Actually I have problems with Altme to synchronize with some recent 
posts I can't see all of them currenly)
Steeve:
4-Oct-2012
Completly changed my mind. It's lot leasier to manage /recur as a 
refinement! 
- eg. safe/recur instead of recur

- no words collision anymore (obviously /recur can't be used as a 
parameter).
Also really short code 

rfunc: func [[catch] spec [block!] body [block!] /local ctx fun][
    spec: append copy spec /recur

    ctx: bind? first second fun: throw-on-error [func spec [recur]]
    change second :fun compose/deep [
        if (in ctx 'recur) [throw/name second (ctx) 'recur]
        while [true][

            set/any [(bind to-block form first ctx ctx)] catch/name [
                return do [(bind/copy body ctx)]
            ] 'recur
        ]
    ]
    :fun
]
Steeve:
5-Oct-2012
;Go back to recur as a function.
;Still recur can't be used as a parameter, local or a refinement.

;This implementation is much more clean (no shitty compose/deep) 
and still very short.

;The collision of words is avoided by the use of singleton functions 
#[function!]
;I'm confident with this one. It could be the last one -_-;


rfunc: func [[catch] spec [block!] body [block!] /local ctx args][
    ctx: bind? first second throw-on-error [
        ;* Temporary function created to retrieve parameters
        ;* and to get a new context for 'recur.
        ;* The context will remain alive (not GC'ed).
        func append copy spec /recur [recur]
    ]
    args: bind to-block form first ctx ctx
    ctx/recur: func spec reduce [

        quote #[function! ['word] [throw/name second bind? word 'recur]] 

        first args ;* may be 'recur if empty specs (still, it's ok)
    ]
    func spec reduce [
        quote #[function! [args body][

            while [true][set/any args catch/name [return do body] 'recur]
        ]] 
        head remove back tail args ;* remove 'recur
        bind/copy body ctx         ;* bound 'recur
    ]
]
Steeve:
5-Oct-2012
;Go back to recur as a function.
;Still recur can't be used as a parameter, local or a refinement.

;This implementation is much more clean (no shitty compose/deep) 
and still very short.

;The collision of words is avoided by the use of singleton functions 
#[function!]
;I'm confident with this one. It could be the last one -_-;


rfunc: func [[catch] spec [block!] body [block!] /local ctx args][
    ctx: bind? first second throw-on-error [
        ;* Temporary function created to retrieve parameters
        ;* and to get a new context for 'recur.
        ;* The context will remain alive (not GC'ed).
        func append copy spec /recur [recur]
    ]
    args: bind to-block form first ctx ctx
    ctx/recur: func spec reduce [

        quote #[function! ['word] [throw/name second bind? word 'recur]] 

        first args ;* may be 'recur if empty specs (still, it's ok)
    ]
    func spec reduce [
        quote #[function! [args body][

            while [true][set/any args catch/name [return do body] 'recur]
        ]] 
        head remove back tail args ;* remove 'recur
        bind/copy body ctx         ;* bound 'recur
    ]
]
Ladislav:
5-Oct-2012
The fact is that the CATCH/NAME+THROW/NAME pair is not ideal for 
this, but I do have a function which might be able to handle even 
the G/RECUR case.
Ladislav:
5-Oct-2012
BTW, Steeve:


    quote #[function! ['word] [throw/name second bind? word 'recur]]

is equivalent to

    func ['word] [throw/name second bind? word 'recur]
Steeve:
5-Oct-2012
; Sorry Ladislav I've stolen your idea one should avoid catch/throw 
interferences

; As an extra (also your idea ?), f/recur and recur are now both 
allowed.

rfunc: func [[catch] spec [block!] body [block!] /local ctx fun][
    ctx: bind? take second fun: throw-on-error [
        func append copy spec /recur reduce ['recur body]
    ]
    insert second :fun reduce [

        quote #[function! [[throw] ctx args 'fun body /local ret][

            if :ctx/recur [ctx/recur: ctx throw/name second ctx 'recur]
            ctx/recur: :fun
            while [true][
                set/any 'ret catch/name [return do body] 'recur

                unless all [value? 'ret block? :ret same? ctx last ret][
                    throw/name get/any 'ret 'recur
                ]
                set/any args ret
            ]
        ]]

        ctx (bind head remove back tail to-block form first ctx ctx) :fun
    ]
    :fun
]
Steeve:
5-Oct-2012
; Ladislav I can't see if you posted new code (WTF Altme)

; So I've tried your idea one should avoid catch/throw interferences

; As an extra (also your idea ?), f/recur and recur are now both 
allowed.

rfunc: func [[catch] spec [block!] body [block!] /local ctx fun][
    ctx: bind? take second fun: throw-on-error [
        func append copy spec /recur reduce ['recur body]
    ]
    insert second :fun reduce [

        quote #[function! [[throw] ctx args 'fun body /local ret][

            if :ctx/recur [ctx/recur: ctx throw/name second ctx 'recur]
            ctx/recur: :fun
            while [true][
                set/any 'ret catch/name [return do body] 'recur

                unless all [value? 'ret block? :ret same? ctx last ret][
                    throw/name get/any 'ret 'recur
                ]
                set/any args ret
            ]
        ]]

        ctx (bind head remove back tail to-block form first ctx ctx) :fun
    ]
    :fun
]
Steeve:
5-Oct-2012
; Ladislav I can't see if you posted new code (WTF Altme)

; So I've tried your idea one should avoid catch/throw interferences

; As an extra (also your idea ?), f/recur and recur are now both 
allowed.

rfunc: func [[catch] spec [block!] body [block!] /local ctx fun][
    ctx: bind? take second fun: throw-on-error [
        func append copy spec /recur reduce ['recur body]
    ]
    insert second :fun reduce [

        quote #[function! [[throw] ctx args 'fun body /local ret][

            if :ctx/recur [ctx/recur: ctx throw/name second ctx 'recur]
            ctx/recur: :fun
            while [true][
                set/any 'ret catch/name [return do body] 'recur

                unless all [value? 'ret block? :ret same? ctx last ret][
                    throw/name get/any 'ret 'recur
                ]
                set/any args ret
            ]
        ]]

        ctx (bind head remove back tail to-block form first ctx ctx) :fun
    ]
    :fun
]
Ladislav:
5-Oct-2012
However, Steeve, you probably do not understand what the problem 
with the

    f: rfunc [x] [if x = 2 [g/recur 3 5]]


code is. The problem in a nutshell is that the G/RECUR call uses 
G/RECUR calling convention and "expects" the G/RECUR call to be used; 
however, the CATCH/NAME+THROW/NAME pair does not respect that and 
actually would do the call of F/RECUR.
Steeve:
5-Oct-2012
This time it's really really my final version T_T
- Both f/recur and recur allowed
- Catch/throw interferences ok.

NB: The code would be simpler in R3 since several workarounds are 
used to correct misbehaviors of object related natives of R2.

Also the lack of the reflexive capability for a function to read 
its own context in a easy way is definitivly a huge miss.

(On can't create anonymous functions without analysing their specs 
first. What a pain)

One would need a reserved word holding the context (like SELF for 
objects).

These shortcomings are making the code too much obfuscated and huge 
for my taste.
I hope it will be corrected in R3..

rfunc: func [
    [catch] spec [block!] body [block!] 
    /local ctx args call-tail
][
    ctx: bind? first second throw-on-error [
        func spec: append copy spec /recur [recur]
    ]
    args: bind head remove back tail to-block form first ctx ctx
    call-tail: func ['word] compose/deep [
        set/any [(args)] second bind? word 
        throw/name (ctx) 'recur
    ]
    ctx/recur: func spec reduce [:call-tail 'recur]
    func spec reduce [
        quote #[function! [
            [throw] 'recur 'call-tail ctx args body /local ret
        ][
            if get/any recur [call-tail :recur]
            set recur get in ctx 'recur
            while [true][
                set/any 'ret catch/name [return do body] 'recur
                unless all [value? 'ret same? :ret ctx][
                    throw/name get/any 'ret 'recur
                ]
                set/any args second ctx
            ]
        ]]
        'recur :call-tail ctx args body
    ]
]

world-name: r3wp

Group: Core ... Discuss core issues [web-public]
BrianH:
18-Dec-2008
Outside of rebcode, you can't goto down, but you can goto up with 
CATCH/name and THROW/name. Of course the structured goto replacements 
still work (RETURN, EXIT, BREAK).
BrianH:
18-Dec-2008
continuing: func [code [block!]] [catch/name code 'continue]

continue: func [/return value [any-type!]] [throw/name get/any 'value 
'continue]
Group: !REBOL3-OLD1 ... [web-public]
Volker:
11-May-2006
;What do i miss?
f: func [
    /local g
] [
    catch/name [
        g: func [n] [
            if n = 0 [
                return 0
            ] 
            if n = 1 [
                throw/name 1 'return-from-f
            ]
        ] 
        g 0 
        g 1 
        g 0
    ] 'return-from-f
] 
probe f
Group: !REBOL3 ... [web-public]
Sunanda:
3-Mar-2010
But 
  exit
_is_ an error on its own.

It is hard to see why ATTEMPTing an error "is never an error on its 
own", especially as trying it by itself in a console does create 
an error.  
  exit  ;; REBOL says is an error
  attempt [exit]  ;; Brian and I disagree if this is an error

it creates a horribly inconsistent mental model.

You could create a trivia/gotcha quiz arond this. Which of these 
stop with an error?
  attempt [throw]
  attempt [throw 0]
  attempt [throw/break]
  attempt [throw/break 0]
  attempt [throw/name]
  attempt [throw/name 0 0]
  attempt [throw/name 0 'x]
BrianH:
3-Mar-2010
And the first two THROW/name examples are wrong too, because of argument 
compatibility.
BrianH:
4-Mar-2010
However, CATCH/name and THROW/name would need the additional memory 
overhead of a single block of words per task in the dynamic solution 
to store the currently handled names.
BrianH:
4-Mar-2010
Except the THROW/name block-of-words thing.
Gabriele:
5-Mar-2010
>> do does [loop 1 [catch [attempt [1 / 0]]]]
== none
>> do does [loop 1 [catch [attempt [exit]]]] 
>> do does [loop 1 [catch [attempt [return 10]]]]
== 10
>> do does [loop 1 [catch [attempt [break]]]]    
>> do does [loop 1 [catch [attempt [break/return 10]]]]
== 10
>> do does [loop 1 [catch [attempt [throw 10]]]]       
== 10
>> do does [loop 1 [catch [attempt [throw/name 10 'something]]]]
== 10
BrianH:
12-Mar-2010
We use THROW/name to write sandboxed replacements for the unwind 
functions, non-local dynamic escape functions, etc. But that only 
works if CATCH doesn't catch it.
BrianH:
13-Mar-2010
The advantage to that is because on those rare occasions when you 
actually need to catch all THROW and THROW/name unwinds, you most 
likely need to catch the rest too. You might note that the emergency 
exceptions - HALT, QUIT/now, errors - aren't proposed to be caught, 
because the cleanup code would likely interfere with error recovery 
code or the debugging process.
BrianH:
13-Mar-2010
Most of the time you really don't want THROW/name caught by code 
that it isn't intended for. When you really want to catch off-topic 
stuff, you want to catch everything.
Group: Core ... Discuss core issues [web-public]
Ladislav:
3-Nov-2010
you mean for this?:

for n 1 5 1 [
	catch/name [
    	if n < 3 [throw/name none 'continue]
    	print n
    ] 'continue
]
Ladislav:
3-Nov-2010
cc: func [[throw] body [block!]] [catch/name body 'continue]
continue: func [[throw]] [throw/name none 'continue]
Ladislav:
3-Nov-2010
A different idea, instantly causing any R2 cycle to "understand continue":

cc: func [
	{convert a cycle body to "understand" CONTINUE}
	body [block!]
] [
	compose/only [catch/name (body) 'continue]
]

continue: func [[throw]] [throw/name none 'continue]

; usage:
for n 1 5 1 cc [
	if n < 3 [continue]
	print n
]
BrianH:
5-Nov-2010
You were focusing on localiity of where the code was written, and 
I was talking of locality in the code that the flow of execution 
goes through at runtime. For instance, #1744 makes it difficult for 
non-local-definition code to do man-in-the-middle attacks or spoofing, 
making it useful for secure mezzanine control flow functions. But 
#1518 prevents you from being able to pass THROW/name through unknown 
code at all, making it useless for making mezzanine control flow 
functions at all. Fixing #1518 is what we do to make #1743 possible, 
and once #1520 is implemented then the arms race will be over, everything 
else could be mezzanine or user-defined.
Group: !REBOL3 Proposals ... For discussion of feature proposals [web-public]
BrianH:
9-Nov-2010
There are real learning and semantic advantages to just going with 
one return model. We just need to make the limitations of whatever 
model we choose easy for regular programmers to workaround if necessary, 
and pick the defaults well so the workarounds won't need to be specified 
as often. The last model satisfies all of those at the expense of 
losing the benefits of dynamic return, and the next to last doesn't 
even lose those, though it does lose some simplicity. Given that 
the remaining benefits of dynamic return can be restored by keeping 
THROW dynamic and fixing the THROW/name bugs, I'm willing to part 
with dynamic return and get back the simplicity.
Maxim:
11-Nov-2010
my only desire in all of this discussion is that trhow/catch is kept 
dynamic and that /name be implemented with context matching and that 
catch doesn't handle throw/name.
Ladislav:
11-Nov-2010
catch doesn't handle throw/name

 - you could always use catch/name [...] 'none versus throw/name 'none, 
 if you did not want to catch other throw/name calls, so this is still 
 just "cosmetic", and surely not serious in any sense I can imagine.
BrianH:
11-Nov-2010
Please don't take my mentioning of downsides as being a statement 
of opinion or some kind of taking sides. I only mention them because 
they are real, and must be considered when picking a certain strategy. 
Both approaches have plusses and minuses. If you want to make a rational 
choice then you need to know the issues - otherwise you are just 
being a fanboy.


For instance, I picked the definitional side for returns, without 
the need for a fallback to dynamic, because of a rational evaluation 
of the algorithmic style of R3's functions. And it wasn't until I 
remembered that the tasking issues had already removed the advantages 
that dynamic scoping has over lexical scoping - we just can't do 
that stuff as much anymore, so it doesn't matter if we don't try. 
The same goes for loops, but to a lesser extent - loops aren't affected 
as much by tasking issues so we can still do code that would benefit 
from dynamic breaks, but it still might be a worthy tradeoff to avoid 
needing an option (since we have no such option). But for THROW, 
especially THROW/name, there are things that you can do with dynamic 
throw that you *can't* do with definitional, and those things would 
have great value, so it's a rational choice to make the tradeoff 
in favor of dynamic.
BrianH:
11-Nov-2010
Well it comes down to this: Functions are defined lexically. Though 
they are called dynamically, they aren't called until after they 
have already been bound, definitionally. But as a side effect of 
tasking, those bindings are stack-relative, and those stacks are 
task-local. But random blocks of code outside of functions are bound 
to object contexts, and those are *not* task-local. So that means 
that the old R2 practice of calling shared blocks of code is a really 
bad idea in R3 if any words are modified, unless there is some kind 
of locking or synchronization. This means that those blocks need 
to be moved into functions if their code is meant to be sharable, 
which means that at least as far as RETURN and EXIT are concerned, 
they can be considered lexically scoped. The advantage that we would 
get from being able to call a shared block of code and explicitly 
return in that block is moot, because we can't really do that much 
anymore. This means that we don't lose anything by switching to definitional 
code that we haven't already lost for other reasons. At least as 
far as functions are concerned, all task-safe code is definitional.


Loops are also defined lexically, more or less, and the rebinding 
ones are also task-safe because they are BIND/copy'd to a selfless 
object context that is only used for that one call and thrown away 
afterwards. And most calls to loops are task-safe anyways because 
they are contained in functions. However, the LOOP, FORALL, FORSKIP 
and WHILE loops do not rebind at the moment. We actually prefer to 
use those particular loops sometimes in R3 code because they can 
be more efficient than *EACH and REPEAT, because they don't have 
that BIND/copy overhead. Other times we prefer to use *EACH or REPEAT, 
in case particular loop fits better, or has high-enough repetitions 
and enough word references that the 27% overhead for stack-local 
word reference is enough to be more than the once-per-loop BIND/copy 
overhead. Since you don't have to move blocks into *loops* to make 
them task-safe, you can use blocks referred to by word to hold code 
that would be shared between different bits of code in the same function. 
This is called manual common subexpression elimination (CSE), and 
is a common optimization trick in advanced REBOL code, because we 
have to hand-optimize REBOL using tricks that the compiler would 
do for us if we were using a compiled language. Also, PARSE rules 
are often called from loops, and they are frequently (and in specific 
cases necessarily) referred to by word instead of lexically nested; 
most of the time these rules can be quite large, maximizing BIND/copy 
overhead, so you definitely don't want to put the extensive ones 
in a FOREACH or a closure.

Switching to definitional break would have three real downsides:

* Every loop would need to BIND/copy, every time the loop is called, 
including the loops that we were explicitly using because they *don't* 
BIND/copy.

* Code that is not nested in the main loop block would not be able 
to break from that loop. And code that is nested in the main loop 
would BIND/copy.

* We can in theory catch unwinds, run some recovery code, and send 
them on their way (hopefully only in native code, see #1521). Definitional 
escapes might be hard or impossible to catch in this way, depending 
on how they are implemented, and that would mean that you couldn't 
recover from breaks anymore.


The upside to definitional break would be that you could skip past 
a loop or two if you wanted to, something you currently can't do. 
Another way to accomplish that would be to add /name options to all 
the loop functions, and that wouldn't have the BIND/copy overhead. 
Or to use THROW or THROW/name.


The situation with THROW is similar to that of the non-binding loops, 
but more so, still task-safe because of functions. But CATCH and 
THROW are typically the most useful in two scenarios:

* Escaping through a lot of levels that would catch dynamic breaks 
or returns.

* Premade custom escape functions that might need to enforce specific 
semantics.


Both of these uses can cause a great deal of difficulty if we switched 
to definitional throw. In the first case, the code is often either 
broken into different functions (and thus not nested), or all dumped 
into a very large set of nested code that we wouldn't want to BIND/copy. 
Remember, the more levels we want to throw past, the more code that 
goes into implementing those levels. In the second case definitional 
throw would usually not work at all because the CATCH and the THROW 
would contained in different functions, and the code that calls the 
function wrapping the THROW would not be nested inside the CATCH. 
So you would either need to rebind every bit of code that called 
the THROW, or the definitional THROW would need to be passed to the 
code that wants to call it like a continuation (similar concept). 
Either way would be really awkward.


On the plus side of dynamic (whatever), at least it's easy to catch 
an unwind for debugging, testing or recovery purposes. For that matter, 
the main advantage of using THROW/name as the basic operation that 
developers can use to make custom dynamic escape functions is that 
we can build in a standard way to catch it and that will work for 
every custom escape that is built with it. The end to the arms race 
of break-through and catch.