Hi everyone, So, I've been struggling to find a method to leverage the concurrency model in Chuck and I keep running up against the same issue: my guts tell me to do things that I can't do because of fact that child processes are alive only as long as the parent and the fact that objects are only passed by reference. Let me unpack this for you. I'm building a granular synthesis class library for Chuck. Most everything is being cast as one of two types of objects: 1) Forkable - a process that takes parameters and can be run or sporked 2) Forker - an object that knows how run a process and will fork it out for you. In order to get over the fact that functions are not (yet) datatypes in Chuck which can be passed to other functions, I'm basically following the example that Kassen, Mike, et al. were discussing in this forum topic: http://electro-music.com/forum/viewtopic.php?highlight=functor&t=23546 My problems always seem to rise from the fact that every Forker (whose job it is to fork a process) also has to be aware of how long that process is going to take or else run the risk of dying before it's child has completed. This leads to infinite passing of responsibility for time and every Forker seems to also need to be a Forkable and have it's own Forker, et cetera ad inifinitum. At higher and higher levels of abstraction, finding the duration of a process BEFORE it runs and figuring out how to handle it, is requiring more and more analysis and feels... for lack of a better word, "wrong" to me. This is compounded by the fact that the objects I'm passing into these functions (methods of other objects) can only be passed by reference and it's increasingly difficult to set-and-forget these processes, since they constantly reference other global objects. The passing by reference is fine, but I find myself wishing I had the choice to make a copy. Now, it's entirely probable that I just haven't caught on yet and am going about this process in a completely backward way. Does anyone here run into similar issues and (even better) have good methods of avoiding them? One idea I have considered is to make a Forker a looping process that receives Events as input, rather than an object with methods. As this would require a(nother) complete re-write, I'm stalling. ;-) So, how do you manage your concurrent processes in Chuck? Cheers, Michael PS: Love dynamic Arrays! Can't wait for those SC/Smalltalk-like array methods! -- http://semiotech.org/wp-prod/ http://semiotech.org/michael
Actually, I should tell you all that I took a look at my code from the past
week and it's a heinous example of software design. While I'm rewriting
this travesty, I leave open the question about methods of managing
concurrent processes in Chuck.
Cheers,
Michael
On Fri, Aug 29, 2008 at 7:01 PM, mike clemow
Hi everyone,
So, I've been struggling to find a method to leverage the concurrency model in Chuck and I keep running up against the same issue: my guts tell me to do things that I can't do because of fact that child processes are alive only as long as the parent and the fact that objects are only passed by reference.
Let me unpack this for you. I'm building a granular synthesis class library for Chuck. Most everything is being cast as one of two types of objects:
1) Forkable - a process that takes parameters and can be run or sporked 2) Forker - an object that knows how run a process and will fork it out for you.
In order to get over the fact that functions are not (yet) datatypes in Chuck which can be passed to other functions, I'm basically following the example that Kassen, Mike, et al. were discussing in this forum topic:
http://electro-music.com/forum/viewtopic.php?highlight=functor&t=23546
My problems always seem to rise from the fact that every Forker (whose job it is to fork a process) also has to be aware of how long that process is going to take or else run the risk of dying before it's child has completed. This leads to infinite passing of responsibility for time and every Forker seems to also need to be a Forkable and have it's own Forker, et cetera ad inifinitum. At higher and higher levels of abstraction, finding the duration of a process BEFORE it runs and figuring out how to handle it, is requiring more and more analysis and feels... for lack of a better word, "wrong" to me.
This is compounded by the fact that the objects I'm passing into these functions (methods of other objects) can only be passed by reference and it's increasingly difficult to set-and-forget these processes, since they constantly reference other global objects. The passing by reference is fine, but I find myself wishing I had the choice to make a copy.
Now, it's entirely probable that I just haven't caught on yet and am going about this process in a completely backward way. Does anyone here run into similar issues and (even better) have good methods of avoiding them?
One idea I have considered is to make a Forker a looping process that receives Events as input, rather than an object with methods. As this would require a(nother) complete re-write, I'm stalling. ;-)
So, how do you manage your concurrent processes in Chuck?
Cheers, Michael
PS: Love dynamic Arrays! Can't wait for those SC/Smalltalk-like array methods!
-- http://semiotech.org/wp-prod/ http://semiotech.org/michael
One way to get around the "shreds killing their children" (oh my God the
children!) problem could be to have some main sporker shred that's always
running and then use a queue and an event like this:
Event sporkEvent;
sporkableQueue;
fun void sporker() {
Sporkable @ sporkable;
while(true) {
sporkEvent => now;
sporkableQueue.popFirst() @=> sporkable;
while (sporkable != null) {
spork ~ sporkable.main();
}
}
}
spork ~ sporker();
fun void sporkShred(Sporkable @ sporkable) {
sporkableQueue.addLast(sporkable);
sporkEvent.signal();
}
...so instead of sporking directly you call sporkShred(sporkable), some
instance of a subclass of Sporkable with a suitable main() implementation.
Of course you need to implement the queue as well.
/Stefan
On Sat, Aug 30, 2008 at 5:28 AM, mike clemow
Actually, I should tell you all that I took a look at my code from the past week and it's a heinous example of software design. While I'm rewriting this travesty, I leave open the question about methods of managing concurrent processes in Chuck.
Cheers, Michael
On Fri, Aug 29, 2008 at 7:01 PM, mike clemow
wrote: Hi everyone,
So, I've been struggling to find a method to leverage the concurrency model in Chuck and I keep running up against the same issue: my guts tell me to do things that I can't do because of fact that child processes are alive only as long as the parent and the fact that objects are only passed by reference.
Let me unpack this for you. I'm building a granular synthesis class library for Chuck. Most everything is being cast as one of two types of objects:
1) Forkable - a process that takes parameters and can be run or sporked 2) Forker - an object that knows how run a process and will fork it out for you.
In order to get over the fact that functions are not (yet) datatypes in Chuck which can be passed to other functions, I'm basically following the example that Kassen, Mike, et al. were discussing in this forum topic:
http://electro-music.com/forum/viewtopic.php?highlight=functor&t=23546
My problems always seem to rise from the fact that every Forker (whose job it is to fork a process) also has to be aware of how long that process is going to take or else run the risk of dying before it's child has completed. This leads to infinite passing of responsibility for time and every Forker seems to also need to be a Forkable and have it's own Forker, et cetera ad inifinitum. At higher and higher levels of abstraction, finding the duration of a process BEFORE it runs and figuring out how to handle it, is requiring more and more analysis and feels... for lack of a better word, "wrong" to me.
This is compounded by the fact that the objects I'm passing into these functions (methods of other objects) can only be passed by reference and it's increasingly difficult to set-and-forget these processes, since they constantly reference other global objects. The passing by reference is fine, but I find myself wishing I had the choice to make a copy.
Now, it's entirely probable that I just haven't caught on yet and am going about this process in a completely backward way. Does anyone here run into similar issues and (even better) have good methods of avoiding them?
One idea I have considered is to make a Forker a looping process that receives Events as input, rather than an object with methods. As this would require a(nother) complete re-write, I'm stalling. ;-)
So, how do you manage your concurrent processes in Chuck?
Cheers, Michael
PS: Love dynamic Arrays! Can't wait for those SC/Smalltalk-like array methods!
-- http://semiotech.org/wp-prod/ http://semiotech.org/michael
-- http://semiotech.org http://semiotech.org/michael
_______________________________________________ chuck-users mailing list chuck-users@lists.cs.princeton.edu https://lists.cs.princeton.edu/mailman/listinfo/chuck-users
-- Release me, insect, or I will destroy the Cosmos!
This solution could be written more prettily if there was some way to wait
on several events, or some more general condition, like so:
(sporkableQueue not empty) => now;
I run into this every now and then.
/Stefan
On Sat, Aug 30, 2008 at 12:01 PM, Stefan Blixt
One way to get around the "shreds killing their children" (oh my God the children!) problem could be to have some main sporker shred that's always running and then use a queue and an event like this:
Event sporkEvent;
sporkableQueue;
fun void sporker() {
Sporkable @ sporkable;
while(true) {
sporkEvent => now;
sporkableQueue.popFirst() @=> sporkable;
while (sporkable != null) {
spork ~ sporkable.main();
}
}
}
spork ~ sporker();
fun void sporkShred(Sporkable @ sporkable) {
sporkableQueue.addLast(sporkable);
sporkEvent.signal();
}
...so instead of sporking directly you call sporkShred(sporkable), some instance of a subclass of Sporkable with a suitable main() implementation. Of course you need to implement the queue as well.
/Stefan
On Sat, Aug 30, 2008 at 5:28 AM, mike clemow
wrote: Actually, I should tell you all that I took a look at my code from the past week and it's a heinous example of software design. While I'm rewriting this travesty, I leave open the question about methods of managing concurrent processes in Chuck.
Cheers, Michael
On Fri, Aug 29, 2008 at 7:01 PM, mike clemow
wrote: Hi everyone,
So, I've been struggling to find a method to leverage the concurrency model in Chuck and I keep running up against the same issue: my guts tell me to do things that I can't do because of fact that child processes are alive only as long as the parent and the fact that objects are only passed by reference.
Let me unpack this for you. I'm building a granular synthesis class library for Chuck. Most everything is being cast as one of two types of objects:
1) Forkable - a process that takes parameters and can be run or sporked 2) Forker - an object that knows how run a process and will fork it out for you.
In order to get over the fact that functions are not (yet) datatypes in Chuck which can be passed to other functions, I'm basically following the example that Kassen, Mike, et al. were discussing in this forum topic:
http://electro-music.com/forum/viewtopic.php?highlight=functor&t=23546
My problems always seem to rise from the fact that every Forker (whose job it is to fork a process) also has to be aware of how long that process is going to take or else run the risk of dying before it's child has completed. This leads to infinite passing of responsibility for time and every Forker seems to also need to be a Forkable and have it's own Forker, et cetera ad inifinitum. At higher and higher levels of abstraction, finding the duration of a process BEFORE it runs and figuring out how to handle it, is requiring more and more analysis and feels... for lack of a better word, "wrong" to me.
This is compounded by the fact that the objects I'm passing into these functions (methods of other objects) can only be passed by reference and it's increasingly difficult to set-and-forget these processes, since they constantly reference other global objects. The passing by reference is fine, but I find myself wishing I had the choice to make a copy.
Now, it's entirely probable that I just haven't caught on yet and am going about this process in a completely backward way. Does anyone here run into similar issues and (even better) have good methods of avoiding them?
One idea I have considered is to make a Forker a looping process that receives Events as input, rather than an object with methods. As this would require a(nother) complete re-write, I'm stalling. ;-)
So, how do you manage your concurrent processes in Chuck?
Cheers, Michael
PS: Love dynamic Arrays! Can't wait for those SC/Smalltalk-like array methods!
-- http://semiotech.org/wp-prod/ http://semiotech.org/michael
-- http://semiotech.org http://semiotech.org/michael
_______________________________________________ chuck-users mailing list chuck-users@lists.cs.princeton.edu https://lists.cs.princeton.edu/mailman/listinfo/chuck-users
-- Release me, insect, or I will destroy the Cosmos!
-- Release me, insect, or I will destroy the Cosmos!
On Sat, Aug 30, 2008 at 11:04 AM, Stefan Blixt
This solution could be written more prettily if there was some way to wait on several events, or some more general condition, like so: (sporkableQueue not empty) => now; I run into this every now and then.
It seems like the queue is a little too generic. You could make a queue which raises the event when items are added. Being able to waiting on multiple events is nice, though I haven't ChucKed enough to want it yet. -- Tom Lieber http://AllTom.com/
Mike, What if each Forkable has/is an Event that is then broadcasted when the Forkable completes? Additionally, a Forkable or Forker could maintain a list of Forkables it has forked, and ensure that each has completed before exiting itself. Below is a quick and dirty sketch of how this might work, combining Forker/Forkable into a single class. As far as pass-by-reference/pass-by-value, I hold the opinion that copying objects is not something that should be implicit in the semantics of an OO system. Mainly because there is always the question of just what is copied and what isn't copied in a default copy operation (shallow vs. deep copy). Shallow bit-wise copies will often break the assumptions made in the class, giving you a broken object. Ditto member-by-member copies. I would claim that its very difficult or impossible to correctly generalize copying semantics across the entire body of allowable classes. Moreover, if such a generalization does exist, it would be too complicated too remember, causing frequent documentation hits or writing a custom copy function. Thus, making copies is something that should be explicitly written on a case-by-case basis for a given class -- with full knowledge of the semantics of the class and the desired semantics of the copy operation. This is something where I believe Java gets it right -- the .clone() member function is protected by default, and must be explicitly publicized by subclasses that override it. So, in long-winded way, I'm suggesting that its not too hard to introduce pass-by-value semantics to your code, provided that you write functions to do the copying. In the future ChucK could conceivably support a notion of a copy constructor, but it would need to support constructors in general first :) spencer // Forkable sketch class Forkable extends Event { 0 => int _isFinished; Forkable @ children[0]; // override this in subclasses fun void run() { } // private; executes run() and performs shred initialization/ cleanup fun void runShell() { run(); for(0 => int i; i < children.size(); i++) { children[i].wait(); } 1 => _isFinished; this.broadcast(); } // call this to spork/fork the Forkable fun void start() { spork ~ runShell(); } // returns 1 if this Forkable has completely finished executing; 0 otherwise fun int isFinished() { return _isFinished; } // protected; starts a new Forkable as a child Forkable. This Forkable is not considered finished // and thus will not exit until all child Forkables are finished. fun void spawn(Forkable @ forkable) { children << forkable; forkable.start(); } // block/pass time until Forkable has finished fun void wait() { if(!isFinished()) this => now; } } class DumbExample2 extends Forkable { static int lastId; lastId++ => int id; fun void run() { Std.rand2f(1,5)::second => now; <<< "done in DumbExample2:", id, "at", now / second, "s" >>>; } } 0 => DumbExample2.lastId; class DumbExample extends Forkable { fun void run() { spawn(new DumbExample2); spawn(new DumbExample2); spawn(new DumbExample2); spawn(new DumbExample2); Std.rand2f(1,5)::second => now; <<< "done in DumbExample", "at", now / second, "s" >>>; } } DumbExample f; f.start(); // do stuff... // ... // ... // okay, now we're done, let's make sure that all child shreds are done before exiting f.wait(); <<< "child Forkable is finished, exiting", "at", now / second, "s" >>>; // end Forkable sketch On Aug 29, 2008, at 4:01 PM, mike clemow wrote:
Hi everyone,
So, I've been struggling to find a method to leverage the concurrency model in Chuck and I keep running up against the same issue: my guts tell me to do things that I can't do because of fact that child processes are alive only as long as the parent and the fact that objects are only passed by reference.
Let me unpack this for you. I'm building a granular synthesis class library for Chuck. Most everything is being cast as one of two types of objects:
1) Forkable - a process that takes parameters and can be run or sporked 2) Forker - an object that knows how run a process and will fork it out for you.
In order to get over the fact that functions are not (yet) datatypes in Chuck which can be passed to other functions, I'm basically following the example that Kassen, Mike, et al. were discussing in this forum topic:
http://electro-music.com/forum/viewtopic.php?highlight=functor&t=23546
My problems always seem to rise from the fact that every Forker (whose job it is to fork a process) also has to be aware of how long that process is going to take or else run the risk of dying before it's child has completed. This leads to infinite passing of responsibility for time and every Forker seems to also need to be a Forkable and have it's own Forker, et cetera ad inifinitum. At higher and higher levels of abstraction, finding the duration of a process BEFORE it runs and figuring out how to handle it, is requiring more and more analysis and feels... for lack of a better word, "wrong" to me.
This is compounded by the fact that the objects I'm passing into these functions (methods of other objects) can only be passed by reference and it's increasingly difficult to set-and-forget these processes, since they constantly reference other global objects. The passing by reference is fine, but I find myself wishing I had the choice to make a copy.
Now, it's entirely probable that I just haven't caught on yet and am going about this process in a completely backward way. Does anyone here run into similar issues and (even better) have good methods of avoiding them?
One idea I have considered is to make a Forker a looping process that receives Events as input, rather than an object with methods. As this would require a(nother) complete re-write, I'm stalling. ;-)
So, how do you manage your concurrent processes in Chuck?
Cheers, Michael
PS: Love dynamic Arrays! Can't wait for those SC/Smalltalk-like array methods!
-- http://semiotech.org/wp-prod/ http://semiotech.org/michael _______________________________________________ chuck-users mailing list chuck-users@lists.cs.princeton.edu https://lists.cs.princeton.edu/mailman/listinfo/chuck-users
Spencer, I really like this idea and plan on implementing it based on your example below. I'll let you all know how I fare.
Thus, making copies is something that should be explicitly written on a case-by-case basis for a given class -- with full knowledge of the semantics of the class and the desired semantics of the copy operation. This is something where I believe Java gets it right -- the .clone() member function is protected by default, and must be explicitly publicized by subclasses that override it. <
This is basically what I have been doing and I see what you mean.
Actually, it's been more of a benchmark for me in the sense that I
feel like I'm doing things in a truly "Chuckian" way when I don't have
to use my copy function. ;-)
What I really want is functions to be objects so that I can pass them
as arguments and return them from other functions. This would make me
very happy.
Cheers,
Michael
On Sun, Aug 31, 2008 at 4:00 AM, Spencer Salazar
Mike, What if each Forkable has/is an Event that is then broadcasted when the Forkable completes? Additionally, a Forkable or Forker could maintain a list of Forkables it has forked, and ensure that each has completed before exiting itself. Below is a quick and dirty sketch of how this might work, combining Forker/Forkable into a single class.
As far as pass-by-reference/pass-by-value, I hold the opinion that copying objects is not something that should be implicit in the semantics of an OO system. Mainly because there is always the question of just what is copied and what isn't copied in a default copy operation (shallow vs. deep copy). Shallow bit-wise copies will often break the assumptions made in the class, giving you a broken object. Ditto member-by-member copies. I would claim that its very difficult or impossible to correctly generalize copying semantics across the entire body of allowable classes. Moreover, if such a generalization does exist, it would be too complicated too remember, causing frequent documentation hits or writing a custom copy function.
Thus, making copies is something that should be explicitly written on a case-by-case basis for a given class -- with full knowledge of the semantics of the class and the desired semantics of the copy operation. This is something where I believe Java gets it right -- the .clone() member function is protected by default, and must be explicitly publicized by subclasses that override it.
So, in long-winded way, I'm suggesting that its not too hard to introduce pass-by-value semantics to your code, provided that you write functions to do the copying. In the future ChucK could conceivably support a notion of a copy constructor, but it would need to support constructors in general first :)
spencer
// Forkable sketch
class Forkable extends Event { 0 => int _isFinished; Forkable @ children[0];
// override this in subclasses fun void run() {
}
// private; executes run() and performs shred initialization/cleanup fun void runShell() { run();
for(0 => int i; i < children.size(); i++) { children[i].wait(); }
1 => _isFinished; this.broadcast(); }
// call this to spork/fork the Forkable fun void start() { spork ~ runShell(); }
// returns 1 if this Forkable has completely finished executing; 0 otherwise fun int isFinished() { return _isFinished; }
// protected; starts a new Forkable as a child Forkable. This Forkable is not considered finished // and thus will not exit until all child Forkables are finished. fun void spawn(Forkable @ forkable) { children << forkable; forkable.start(); }
// block/pass time until Forkable has finished fun void wait() { if(!isFinished()) this => now; } }
class DumbExample2 extends Forkable { static int lastId; lastId++ => int id;
fun void run() { Std.rand2f(1,5)::second => now; <<< "done in DumbExample2:", id, "at", now / second, "s" >>>; } }
0 => DumbExample2.lastId;
class DumbExample extends Forkable { fun void run() { spawn(new DumbExample2); spawn(new DumbExample2); spawn(new DumbExample2); spawn(new DumbExample2);
Std.rand2f(1,5)::second => now; <<< "done in DumbExample", "at", now / second, "s" >>>; } }
DumbExample f;
f.start();
// do stuff... // ... // ...
// okay, now we're done, let's make sure that all child shreds are done before exiting
f.wait();
<<< "child Forkable is finished, exiting", "at", now / second, "s" >>>;
// end Forkable sketch
On Aug 29, 2008, at 4:01 PM, mike clemow wrote:
Hi everyone,
So, I've been struggling to find a method to leverage the concurrency model in Chuck and I keep running up against the same issue: my guts tell me to do things that I can't do because of fact that child processes are alive only as long as the parent and the fact that objects are only passed by reference.
Let me unpack this for you. I'm building a granular synthesis class library for Chuck. Most everything is being cast as one of two types of objects:
1) Forkable - a process that takes parameters and can be run or sporked 2) Forker - an object that knows how run a process and will fork it out for you.
In order to get over the fact that functions are not (yet) datatypes in Chuck which can be passed to other functions, I'm basically following the example that Kassen, Mike, et al. were discussing in this forum topic:
http://electro-music.com/forum/viewtopic.php?highlight=functor&t=23546
My problems always seem to rise from the fact that every Forker (whose job it is to fork a process) also has to be aware of how long that process is going to take or else run the risk of dying before it's child has completed. This leads to infinite passing of responsibility for time and every Forker seems to also need to be a Forkable and have it's own Forker, et cetera ad inifinitum. At higher and higher levels of abstraction, finding the duration of a process BEFORE it runs and figuring out how to handle it, is requiring more and more analysis and feels... for lack of a better word, "wrong" to me.
This is compounded by the fact that the objects I'm passing into these functions (methods of other objects) can only be passed by reference and it's increasingly difficult to set-and-forget these processes, since they constantly reference other global objects. The passing by reference is fine, but I find myself wishing I had the choice to make a copy.
Now, it's entirely probable that I just haven't caught on yet and am going about this process in a completely backward way. Does anyone here run into similar issues and (even better) have good methods of avoiding them?
One idea I have considered is to make a Forker a looping process that receives Events as input, rather than an object with methods. As this would require a(nother) complete re-write, I'm stalling. ;-)
So, how do you manage your concurrent processes in Chuck?
Cheers, Michael
PS: Love dynamic Arrays! Can't wait for those SC/Smalltalk-like array methods!
-- http://semiotech.org/wp-prod/ http://semiotech.org/michael _______________________________________________ chuck-users mailing list chuck-users@lists.cs.princeton.edu https://lists.cs.princeton.edu/mailman/listinfo/chuck-users
_______________________________________________ chuck-users mailing list chuck-users@lists.cs.princeton.edu https://lists.cs.princeton.edu/mailman/listinfo/chuck-users
On Mon, Sep 1, 2008 at 2:06 PM, mike clemow
What I really want is functions to be objects so that I can pass them as arguments and return them from other functions. This would make me very happy.
Closures would be cool, but would it be possible to do something similar by using objects that have member functions, and passing those? Steve
Stephen Sinclair wrote:
mike clemow wrote:
What I really want is functions to be objects so that I can pass them as arguments and return them from other functions. This would make me very happy.
Closures would be cool, but would it be possible to do something similar by using objects that have member functions, and passing those?
That is what the original proposal (forum link above) suggests as a workaround. michael
Well, yes, exactly. That's what this forum topic I mentioned is all about:
http://electro-music.com/forum/viewtopic.php?highlight=functor&t=23546
Functors, I think they're called. Essentially, the idea of wrapping
the concept of a "runnable process"--or in this case a sporkable
one--into a class of some sort with a .run() method that is always
overridden is in order to provide a way to pass any process to another
class that can run (or spork) them as children. You can get a lot of
functional mileage out of that, but it's confusing and awkward as I'm
finding out.
-Mike
On Tue, Sep 2, 2008 at 10:55 AM, Stephen Sinclair
On Mon, Sep 1, 2008 at 2:06 PM, mike clemow
wrote: What I really want is functions to be objects so that I can pass them as arguments and return them from other functions. This would make me very happy.
Closures would be cool, but would it be possible to do something similar by using objects that have member functions, and passing those?
Steve _______________________________________________ chuck-users mailing list chuck-users@lists.cs.princeton.edu https://lists.cs.princeton.edu/mailman/listinfo/chuck-users
Yes, that as well. That's why I'm only trying to encapsulate enough
of it to get around the annoying bits and then use Chuck in the way
that Chuck likes to be used.
-m
On Tue, Sep 2, 2008 at 11:07 AM, Michael Heuer
mike clemow wrote:
...it's confusing and awkward as I'm finding out.
I would like to add overly verbose and difficult to manage without namespace support to the list. :)
michael _______________________________________________ chuck-users mailing list chuck-users@lists.cs.princeton.edu https://lists.cs.princeton.edu/mailman/listinfo/chuck-users
participants (6)
-
Michael Heuer
-
mike clemow
-
Spencer Salazar
-
Stefan Blixt
-
Stephen Sinclair
-
Tom Lieber