Re: Short-circuiting user-defined operators
On Tue, Apr 01, 2003 at 08:26:38PM -0500, Joe Gottman wrote: :Is there any way to write a user-defined operator so that it : short-circuits, like and || ? This might be function trait, for : instance, : : sub infix:!! ($lhs, $rhs) is short_circuit {...} Honestly, you guys. You make things so hard for yourselves. I don't see why that wouldn't just be: sub infix:!! ($lhs, rhs) is equiv(infix:||) {...} All closures are lazy by definition, and the rhs forces the right side of !! to be a closure. At worst, we'd have to change sub to macro to give a special parsing rule to the right side. Larry
Re: Short-circuiting user-defined operators
Dave Whipp wrote: Joe Gottman wrote: Alternatively, there might be a new parameter type that indicates that the parameter is not evaluated immediately: sub infix:!! ($lsh, $rhs is deferred) {...} A nice concept! So nice, in fact, that it would be a shame to limit it to function args. I could see myself writing: my $a is lazy := expensive_fn1(...); my $b is lazy := expensive_fn2(...); print rand ?? $a :: $b; I'm not sure quite how the syntax would work (should I be binding or assigning?); but there are definitely cases where this ability is useful. Given this capability, defered evaluation of function args would be trivial. Another, very different, situation where laziness is good is to abstract fork/join situations: my $a is lazy_thread := expensive_fn1(...); my $b is lazy_thread := expensive_fn2(...); print $a + $b; In this scenario, each expensive evaluation would be launched as a separate thread. When the resulting value is later used, then the thread would be joined, blocking if the thread is not complete. This is very worth pursuing. I can see two ways to handle implicit threading -- at the object level, or at the sub level. Object Threading: - my Int $a is threaded = expensive_fn1(...); ... print $a; # Implicitly joins $a.thread() Sub Threading: -- sub expensive_fn1(...) is threaded {...} my Int $a = expensive_fn1(); ... print $a; The sub threading version seems, at first blush, easier to implement -- since if the variable requires a thread, then things like := binding, and expression evaluation, get more complex. Also, of course, it seems easier for developers to grok the concept of what's in parallel and what's not when using the Sub Threading approach. But what to do about matrix arithmetic and other simple threadable tasks? sub m_add(@a, @b) { my @result; my $i, $j; @result = @a; for @result - $i {:is threaded # Thread this block? for @result[$i]; @b - $j; $b { $j += $b; } } } Conversely, I have absolutely no idea what to do about threading on a per-data-object basis: sub m_add(@a is threaded, @b) { ... What can the compiler figure out about threading @a? } This is one of those cases where the really smart guys who used to spend time recoding the fortran compilers for Vaxen might be able to clue us in on the cool tricks for vector processing, but how generalizable will it all be? (And, of course, is the internal representation amenable to this?) None of this addresses the issue of explicit threading -- cases where the user wants to create and control her own threads. =Austin
Re: Short-circuiting user-defined operators
--- Austin Hastings [EMAIL PROTECTED] wrote: Dave Whipp wrote: Joe Gottman wrote: Getting deep -- sorry. :) Alternatively, there might be a new parameter type that indicates that the parameter is not evaluated immediately: sub infix:!! ($lsh, $rhs is deferred) {...} If the standard is pass-by-ref it wouldn't trip any side effects unless you did so in your code, right? So is this necessary? Next author A nice concept! So nice, in fact, that it would be a shame to limit it to function args. I could see myself writing: my $a is lazy := expensive_fn1(...); my $b is lazy := expensive_fn2(...); print rand ?? $a :: $b; I could certainly see some sweet uses for this. Any input from the design team on whether this is a pipe dream or not? I'm not sure quite how the syntax would work (should I be binding or assigning?); I'd say binding was right. $a is an implicit call to expensive_fn1(), right? But that just looks like a tie to me. Maybe I misread it. but there are definitely cases where this ability is useful. Given this capability, defered evaluation of function args would be trivial. Another, very different, situation where laziness is good is to abstract fork/join situations: my $a is lazy_thread := expensive_fn1(...); my $b is lazy_thread := expensive_fn2(...); print $a + $b; In this scenario, each expensive evaluation would be launched as a separate thread. When the resulting value is later used, then the thread would be joined, blocking if the thread is not complete. So in this case you're saying to thread and execute the functions *now* so that $a and $b can be coprocessing? That's different from the above example. Sounds like a designer's nightmare, but a beautiful concept if the language could take advantage of implicit threading that easily. Next author This is very worth pursuing. I can see two ways to handle implicit threading -- at the object level, or at the sub level. Object Threading: - my Int $a is threaded = expensive_fn1(...); ... print $a; # Implicitly joins $a.thread() Sub Threading: -- sub expensive_fn1(...) is threaded {...} my Int $a = expensive_fn1(); ... print $a; The sub threading version seems, at first blush, easier to implement -- since if the variable requires a thread, then things like := binding, and expression evaluation, get more complex. Agreed, though I'd say (in fact I did, abover :) that := binding would probably be necessary for that sort of per-object threadingwhich kind of makes it equivelent to the per-sub threading. If the sub is threaded and you bind it to an object, then that's a compile-time bind, right? pre-spawning the thread, maybe? Then when the runtime call occurs the thread receives the relevant arg values and starts proceccing them immediately, etc. Still sounds like an elaborate tie() to me, but a beautiful way to do it. Also, of course, it seems easier for developers to grok the concept of what's in parallel and what's not when using the Sub Threading approach. Agreed, though anyone using threading this way should obviously be VERY careful about it threads are not something to do accidentally. I might even say that such ability should only be available upon explicit pragmatic request use threads :implicit; # maybe? use implicit :threads; # better? or is that a horrifically inappropriate use of Larry's colon? :) But what to do about matrix arithmetic and other simple threadable tasks? sub m_add(@a, @b) { my @result; my $i, $j; @result = @a; for @result - $i {:is threaded # Thread this block? for @result[$i]; @b - $j; $b { $j += $b; } } } isn't $i an element alias rather than an index there? Basically, I just failed to parse all that. Sorry -- internal P6 parser still incomplete. Conversely, I have absolutely no idea what to do about threading on a per-data-object basis: sub m_add(@a is threaded, @b) { ... What can the compiler figure out about threading @a? } I'd say this is unlikely to work, but again, I haven't exactly thought it all through very thoroughly. This is one of those cases where the really smart guys who used to spend time recoding the fortran compilers for Vaxen might be able to clue us in on the cool tricks for vector processing, but how generalizable will it all be? (And, of course, is the internal representation amenable to this?) None of this addresses the issue of explicit threading -- cases where the user wants to create and control her own threads. Or how they would interact. If you're doing implicit threading in a program using explicit threading, things could get REALLY tang-toungled __ Do you Yahoo!? Yahoo! Tax Center - File online, calculators, forms, and more http://tax.yahoo.com
Re: Short-circuiting user-defined operators
Paul wrote: --- Austin Hastings [EMAIL PROTECTED] wrote: Dave Whipp wrote: Joe Gottman wrote: Getting deep -- sorry. :) Alternatively, there might be a new parameter type that indicates that the parameter is not evaluated immediately: sub infix:!! ($lsh, $rhs is deferred) {...} If the standard is pass-by-ref it wouldn't trip any side effects unless you did so in your code, right? So is this necessary? There are two reasonable semantics for deferred parameters: 1) lazy evaluation with caching, where the evaluation of the actual expression in the call is deferred until the sub actauly makes use of it and the result is then cached and reused as necessary. Any side effects happen only once. 2) ALGOL style pass by name, where the actual expression from the call is turned into a clouser called a thunk which is called when ever the sub access the parameter. Note that the thunk may need to be an lvalue clouser to handle is rw paramenters. Side effects happen each time the thunk is called. Also changes to the thunks environment can effect its value when called. Either of those can have threading added as well. -- Mark Biggar [EMAIL PROTECTED] [EMAIL PROTECTED]
Re: Short-circuiting user-defined operators
--- Paul [EMAIL PROTECTED] wrote: But what to do about matrix arithmetic and other simple threadable tasks? sub m_add(@a, @b) { my @result; my $i, $j; @result = @a; for @result - $i {:is threaded # Thread this block? for @result[$i]; @b - $j; $b { $j += $b; } } } isn't $i an element alias rather than an index there? Yeah, but I'm assuming a 2d matrix. @result[$i] is another array. Basically, I just failed to parse all that. Sorry -- internal P6 parser still incomplete. No, just bad writing on my part. Sorry. Conversely, I have absolutely no idea what to do about threading on a per-data-object basis: sub m_add(@a is threaded, @b) { ... What can the compiler figure out about threading @a? } I'd say this is unlikely to work, but again, I haven't exactly thought it all through very thoroughly. I'm pretty sure it *could* work. Likewise, I'm sure we could live without this for the first release, if we but grab the namespace we think we'll need in advance. That's why this stuff should all be hints to the interpreter -- what the box is actually capable of is not something we can know. Certainly it's trivial to thread basic vector processing: 1. Get max# of useful threads (#cpu's - 1)? 2. Every computation of a threaded vector member gets allocated to a new thread, round-robin. 3. Any read of a threaded vector member forces a join on the allocated thread before proceeding. This isn't optimal, but it would work. But knowing how much benefit you're going to see will be part of the equation, too. So you want the runtime to balance the startup/shutdown cost versus the execution cost, if possible. Also, you want to favor threading when a blocking operation might occur in the threadable code. That kind of stuff. This is one of those cases where the really smart guys who used to spend time recoding the fortran compilers for Vaxen might be able clue us in on the cool tricks for vector processing, but how generalizable will it all be? (And, of course, is the internal representation amenable to this?) None of this addresses the issue of explicit threading -- cases where the user wants to create and control her own threads. Or how they would interact. If you're doing implicit threading in a program using explicit threading, things could get REALLY tang-toungled Implicit threading must be transparent to the coder, or it's not implicit, icit? So if I have two explicit threads: my ViewControlTask = new Task(...); my ViewControlThread = new Thread(priority = 1, task = ViewControlTask); my ComputationTask = new Task(...); my ComputationThread = new Thread(priority = 19, task = ComputationTask); Anything which happens *implicitly* in the ComputationTask must be invisible to the coder. So the task will automatically be split and automatically be joined (or not) based on the convenience of the interpreter -- the only thing I'm doing in code is giving hints about what can/should be split into threads. So ideally, Parrot notices that I'm doing threads, or maybe that the interpreter thinks that threads are going to be valuable. So it engages the threadomizer and creates 8 OS level threads, one per CPU. Then perl queries parrot, gets the number of threads created, and allocates explicit threads and implicit threads accordingly -- this is what I don't know how to do. So there's a mixture: ViewControlTask - - - --*-- - * - + implicit thread 1 - -/ \- --/ ComputationTask - - - - - * - - + implicit thread 1 - - - ---/ + implicit thread 2 - - - ---/ + implicit thread 3 - - - ---/ + implicit thread 4 - - - ---/ =Austin
Re: Short-circuiting user-defined operators
--- Austin Hastings [EMAIL PROTECTED] wrote: --- Paul [EMAIL PROTECTED] wrote: But what to do about matrix arithmetic and other simple threadable tasks? sub m_add(@a, @b) { my @result; my $i, $j; @result = @a; for @result - $i {:is threaded # Thread this block? for @result[$i]; @b - $j; $b { $j += $b; } } } isn't $i an element alias rather than an index there? Yeah, but I'm assuming a 2d matrix. @result[$i] is another array. Ah. I should have seen that. (BTW, I personally find it slightly amusing and certainly useful to reformat code to minimize space when it's a quote of a quote of a quote, but still offer apologies for the resulting mishmash.) Basically, I just failed to parse all that. Sorry -- internal P6 parser still incomplete. No, just bad writing on my part. Sorry. I disagree. Once I know what it's doing, it looks fine. :) Conversely, I have absolutely no idea what to do about threading on a per-data-object basis: sub m_add(@a is threaded, @b) { ... What can the compiler figure out about threading @a? } I'd say this is unlikely to work, but again, I haven't exactly thought it all through very thoroughly. I'm pretty sure it *could* work. Likewise, I'm sure we could live without this for the first release, if we but grab the namespace we think we'll need in advance. That's why this stuff should all be hints to the interpreter -- what the box is actually capable of is not something we can know. ah -- and if it isn't feasable, the compiler just ignores those hints, builds it the old-fashioned way, and goes on about it's business. If the hardware and the OS provide sufficient support, on the other hand, the compiler might take advantage of them when the code suggests it. Ok, I could see that. Certainly it's trivial to thread basic vector processing: 1. Get max# of useful threads (#cpu's - 1)? 2. Every computation of a threaded vector member gets allocated to a new thread, round-robin. 3. Any read of a threaded vector member forces a join on the allocated thread before proceeding. This isn't optimal, but it would work. But knowing how much benefit you're going to see will be part of the equation, too. So you want the runtime to balance the startup/shutdown cost versus the execution cost, if possible. Also, you want to favor threading when a blocking operation might occur in the threadable code. That kind of stuff. More nightmares for the designers. :) Maybe that's P8? :) This is one of those cases where the really smart guys who used to spend time recoding the fortran compilers for Vaxen might be able clue us in on the cool tricks for vector processing, but how generalizable will it all be? (And, of course, is the internal representation amenable to this?) None of this addresses the issue of explicit threading -- cases where the user wants to create and control her own threads. Or how they would interact. If you're doing implicit threading in a program using explicit threading, things could get REALLY tang-toungled Implicit threading must be transparent to the coder, or it's not implicit, icit? lol -- but I don't necessarily *totally* agree. I make explicit use of implicit behavior a little too much of the time, depending on what you mean. I think the common term for it is cryptocontext. :) Still, your point is valid. Implicit threading (or anything else) is something done quietly, like implicit dereferencing of arrays. I was just thinking that if the user is using explicit threads, he might do things that implicitly interfere with or receive interferance from implicit threads but then, I can't exactly come up with an example, so the point is probably entirely moot. So if I have two explicit threads: my ViewControlTask = new Task(...); my ViewControlThread = new Thread(priority = 1, task = ViewControlTask); my ComputationTask = new Task(...); my ComputationThread = new Thread(priority = 19, task = ComputationTask); Anything which happens *implicitly* in the ComputationTask must be invisible to the coder. So the task will automatically be split and automatically be joined (or not) based on the convenience of the interpreter -- the only thing I'm doing in code is giving hints about what can/should be split into threads. Ok. I still say that should be pragmatic, though. Easy enough in P6 to Cuse implicit 'threads'; and have the language magically morph to add those hints to the rest of the compilation process. BTW, what *is* the applicability of adverbs to Cuse statements? Is that entirely bogus? So ideally, Parrot notices that I'm doing threads, or maybe that the interpreter thinks that threads are going to be valuable. So it engages the threadomizer and creates 8 OS level threads, one per CPU. Then perl queries parrot, gets the number of threads
Re: Short-circuiting user-defined operators
Paul wrote: --- Austin Hastings [EMAIL PROTECTED] wrote: Dave Whipp wrote: Joe Gottman wrote: Getting deep -- sorry. :) Alternatively, there might be a new parameter type that indicates that the parameter is not evaluated immediately: sub infix:!! ($lsh, $rhs is deferred) {...} If the standard is pass-by-ref it wouldn't trip any side effects unless you did so in your code, right? So is this necessary? There are two reasonable semantics for deferred parameters: 1) lazy evaluation with caching, where the evaluation of the actual expression in the call is deferred until the sub actauly makes use of it and the result is then cached and reused as necessary. Any side effects happen only once. 2) ALGOL style pass by name, where the actual expression from the call is turned into a clouser called a thunk which is called when ever the sub access the parameter. Note that the thunk may need to be an lvalue clouser to handle is rw paramenters. Side effects happen each time the thunk is called. Also changes to the thunks environment can effect its value when called. I think (2) would be best. Because of: while $a $b { ... } That wouldn't be possible with just evaluating it once. I like the interface better, too (for the writer of Cwhile), but that's just me. Luke
Re: Short-circuiting user-defined operators
--- Luke Palmer [EMAIL PROTECTED] wrote: Paul wrote: --- Austin Hastings [EMAIL PROTECTED] wrote: Dave Whipp wrote: Joe Gottman wrote: Getting deep -- sorry. :) Alternatively, there might be a new parameter type that indicates that the parameter is not evaluated immediately: sub infix:!! ($lsh, $rhs is deferred) {...} If the standard is pass-by-ref it wouldn't trip any side effects unless you did so in your code, right? So is this necessary? There are two reasonable semantics for deferred parameters: 1) lazy evaluation with caching, where the evaluation of the actual expression in the call is deferred until the sub actauly makes use of it and the result is then cached and reused as necessary. Any side effects happen only once. 2) ALGOL style pass by name, where the actual expression from the call is turned into a clouser called a thunk which is called when ever the sub access the parameter. Note that the thunk may need to be an lvalue clouser to handle is rw paramenters. Side effects happen each time the thunk is called. Also changes to the thunks environment can effect its value when called. I think (2) would be best. Because of: while $a $b { ... } That wouldn't be possible with just evaluating it once. I like the interface better, too (for the writer of Cwhile), but that's just me. Since parms are defaulting to reference, could I just say: sub foo($arg) { while $arg = 0 { print $arg; } } foo( \{ $i-- } ); or: foo \{ $i-- }; or: foo {$i--}; and get that behavior? Is that a good idea? Regardless, consider: $a || $b $c This translates to: infix:||($a, infix:($b, $c)) so if I want to write infix:!! I could do: infix:!!($left, right is expression) is equiv(infix:||) { return unless predicate($left); return $left right; } But this have the unfortunate side-effect of requiring that all use of the !! operator take place after the declaration -- not the end of the world except when you want to overload a known symbol and make a short-circuit bitwise or operator that does something other than bitwise-or, perhaps. In which case, go code C++, or write a macro, or get your declaration to the top of the pile. But this WILL impact the code generator/optimizer. Perhaps P6 needs the notion of sequence points... Luke =Austin
Re: Short-circuiting user-defined operators
- Original Message - From: Luke Palmer [EMAIL PROTECTED] To: [EMAIL PROTECTED] Cc: [EMAIL PROTECTED] Sent: Thursday, April 03, 2003 6:39 PM Subject: Re: Short-circuiting user-defined operators Paul wrote: --- Austin Hastings [EMAIL PROTECTED] wrote: Dave Whipp wrote: Joe Gottman wrote: Getting deep -- sorry. :) Alternatively, there might be a new parameter type that indicates that the parameter is not evaluated immediately: sub infix:!! ($lsh, $rhs is deferred) {...} If the standard is pass-by-ref it wouldn't trip any side effects unless you did so in your code, right? So is this necessary? There are two reasonable semantics for deferred parameters: 1) lazy evaluation with caching, where the evaluation of the actual expression in the call is deferred until the sub actauly makes use of it and the result is then cached and reused as necessary. Any side effects happen only once. 2) ALGOL style pass by name, where the actual expression from the call is turned into a clouser called a thunk which is called when ever the sub access the parameter. Note that the thunk may need to be an lvalue clouser to handle is rw paramenters. Side effects happen each time the thunk is called. Also changes to the thunks environment can effect its value when called. I think (2) would be best. Because of: while $a $b { ... } That wouldn't be possible with just evaluating it once. I like the interface better, too (for the writer of Cwhile), but that's just me. I prefer 1). When I posted the original thread I wanted to emulate the effect of $a $b or ($a) ?? $b :: $c. The parameters in these might get evaluated zero or one times, but not more than once. Having a side effect happen more than once brings back painful memories of C-style macros. Joe Gottman
Re: Short-circuiting user-defined operators
Joe Gottman wrote: There are two reasonable semantics for deferred parameters: 1) lazy evaluation with caching, where the evaluation of the actual expression in the call is deferred until the sub actauly makes use of it and the result is then cached and reused as necessary. Any side effects happen only once. 2) ALGOL style pass by name, where the actual expression from the call is turned into a clouser called a thunk which is called when ever the sub access the parameter. Note that the thunk may need to be an lvalue clouser to handle is rw paramenters. Side effects happen each time the thunk is called. Also changes to the thunks environment can effect its value when called. I think (2) would be best. Because of: while $a $b { ... } That wouldn't be possible with just evaluating it once. I like the interface better, too (for the writer of Cwhile), but that's just me. I prefer 1). When I posted the original thread I wanted to emulate the effect of $a $b or ($a) ?? $b :: $c. The parameters in these might get evaluated zero or one times, but not more than once. Having a side effect happen more than once brings back painful memories of C-style macros. I tend to agree; but I note that it is possible to implement (1) over the semantics of (2) by simply assigning to a tmp variable. The reverse is not true. So if only one of the two is provided, then it should be (2). Multiple properties can be applied, so is deferred is cached would give (1). Dave. -- http://dave.whipp.name
Re: Short-circuiting user-defined operators
Is there any specific reason this was a reply to Michael Lazarro's Re: == vs. eq dated Tue, 1 Apr 2003 16:30:00 -0800 ? (What I mean is, PLEASE don't use reply when you're not replying at all) -- Matthijs van Duin -- May the Forth be with you!
Short-circuiting user-defined operators
Is there any way to write a user-defined operator so that it short-circuits, like and || ? This might be function trait, for instance, sub infix:!! ($lhs, $rhs) is short_circuit {...} Alternatively, there might be a new parameter type that indicates that the parameter is not evaluated immediately: sub infix:!! ($lsh, $rhs is deferred) {...} In this case, it might be possible to make ordinary functions short-circuit also sub conditional(bool $condition, $trueCase is deferred, $falseCase is deferred) { return ($condition) ?? $trueCase :: $falseCase; } I have no idea how difficult it would be to implement either of these concepts. Also, if a parameter is deferred, would we need a new keyword to say when to actually evaluate it? Joe Gottman
Re: Short-circuiting user-defined operators
Joe Gottman wrote: Alternatively, there might be a new parameter type that indicates that the parameter is not evaluated immediately: sub infix:!! ($lsh, $rhs is deferred) {...} A nice concept! So nice, in fact, that it would be a shame to limit it to function args. I could see myself writing: my $a is lazy := expensive_fn1(...); my $b is lazy := expensive_fn2(...); print rand ?? $a :: $b; I'm not sure quite how the syntax would work (should I be binding or assigning?); but there are definitely cases where this ability is useful. Given this capability, defered evaluation of function args would be trivial. Another, very different, situation where laziness is good is to abstract fork/join situations: my $a is lazy_thread := expensive_fn1(...); my $b is lazy_thread := expensive_fn2(...); print $a + $b; In this scenario, each expensive evaluation would be launched as a separate thread. When the resulting value is later used, then the thread would be joined, blocking if the thread is not complete.
Re: Short-circuiting user-defined operators
Dave Whipp writes: Another, very different, situation where laziness is good is to abstract fork/join situations: my $a is lazy_thread := expensive_fn1(...); my $b is lazy_thread := expensive_fn2(...); print $a + $b; In this scenario, each expensive evaluation would be launched as a separate thread. When the resulting value is later used, then the thread would be joined, blocking if the thread is not complete. That is gorgeous. Clazy_thread isn't a good name, but it's a marvelous concept. Thanks. Luke