1 Function : AbstractFunction {
3 // a Function is what you get when you write a FunctionDef in your code.
4 // it consists of the function's code and the variables in its defining context
6 *new { ^this.shouldNotImplement(thisMethod) }
9 isClosed { ^def.sourceCode.notNil }
11 storeOn { arg stream; stream << (def.sourceCode ? "{ \"open Function\" }"); }
12 archiveAsCompileString { ^true }
13 archiveAsObject { ^true }
14 checkCanArchive { if (def.sourceCode.isNil) { "cannot archive open Functions".warn } }
18 choose { ^this.value }
20 update { |obj, what ... args| this.value(obj, what, *args) }
26 // evaluate a function with args
29 valueArray { arg ... args;
31 // evaluate a function, if the last argument is an array it will be
32 // expanded into separate args.
36 valueEnvir { arg ... args;
38 // evaluate a function with args.
39 // unsupplied argument names are looked up in the currentEnvironment
42 valueArrayEnvir { arg ... args;
43 _FunctionValueArrayEnvir
44 // evaluate a function, if the last argument is an array it will be
45 // expanded into separate args.
46 // unsupplied argument names are looked up in the currentEnvironment
49 functionPerformList { arg selector, arglist;
54 valueWithEnvir { arg envir;
56 if(envir.isNil) { ^this.value };
57 prototypeFrame = def.prototypeFrame.copy;
59 def.argNames.do { |name,i|
60 var val = envir[name];
61 val !? { prototypeFrame[i] = val };
63 // postf("argNames: % prototypeFrame: %\n", def.argNames, prototypeFrame);
65 // evaluate a function, using arguments from the supplied environment
66 // slightly faster than valueEnvir and does not replace the currentEnvironment
67 ^this.valueArray(prototypeFrame)
70 performWithEnvir { |selector, envir|
71 if(selector === \value) { ^this.valueWithEnvir(envir) };
72 ^super.performWithEnvir(selector, envir)
75 performKeyValuePairs { |selector, pairs|
77 if(selector !== \value) {
78 ^this.superPerform(\performKeyValuePairs, pairs)
81 envir = this.def.makeEnvirFromArgs;
82 envir.putPairs(pairs);
84 ^this.valueWithEnvir(envir)
87 numArgs { ^def.numArgs } // return number of arguments to the function
88 numVars { ^def.numVars } // return number of variables in the function
89 varArgs { ^def.varArgs } // return boolean whether function has ellipsis argument
92 // loop is supported magically by the compiler,
93 // thus it can be implemented in terms of itself
98 ^this.value {|val| ^val };
103 // result = this.value #{|val| Break(val).throw };
105 // if (error.class == Break) {
123 n.do {|i| sum = sum + this.value(i) };
127 defer { arg delta = 0;
128 if (delta == 0 and: {this.canCallOS}) {
131 AppClock.sched(delta, { this.value; nil })
135 thunk { ^Thunk(this) }
138 transformEvent { arg event;
142 // ControlView support
143 set { arg ... args; ^this.valueArray(args) }
144 get { arg prevVal; ^prevVal }
146 fork { arg clock, quant, stackSize;
147 ^Routine(this, stackSize).play(clock, quant);
150 forkIfNeeded { arg clock, quant, stackSize;
151 if(thisThread.isKindOf(Routine), this, { ^this.fork(clock, quant, stackSize) });
156 awake { arg beats, seconds, clock;
157 var time = seconds; // prevent optimization
158 ^this.value(beats, seconds, clock)
161 cmdPeriod { this.value }
164 bench { arg print = true;
166 var t0 = Main.elapsedTime;
168 dt = Main.elapsedTime - t0;
169 if (print) { Post << "time to run: " << dt << " seconds.\n"; }
173 protect { arg handler;
176 if (result.isException) {
177 handler.value(result);
180 handler.value; // argument should be nil if there was no exception.
186 var result = this.prTry;
187 if (result.isException) { ^handler.value(result); }
191 var result, thread = thisThread;
192 var next = thread.exceptionHandler,
193 wasInProtectedFunc = Exception.inProtectedFunction;
194 thread.exceptionHandler = {|error|
195 thread.exceptionHandler = next; // pop
198 Exception.inProtectedFunction = true;
200 Exception.inProtectedFunction = wasInProtectedFunc;
201 thread.exceptionHandler = next; // pop
205 handleError { arg error; ^this.value(error) }
207 case { arg ... cases;
208 cases = [this] ++ cases;
209 cases.pairsDo { | test, trueFunc |
210 if (test.value) { ^trueFunc.value };
212 if (cases.size.odd) { ^cases.last.value };
219 matchItem { arg item;
225 performDegreeToKey { arg scaleDegree, stepsPerOctave = 12, accidental = 0;
226 ^this.value(scaleDegree, stepsPerOctave, accidental)
229 // multichannel expand function return values
232 if(def.argNames.isNil) { ^this };
233 ^{ |... args| args.flop.collect(this.valueArray(_)) }
238 var func = this.makeFlopFunc;
240 func.valueArrayEnvir(args).collect(this.valueArray(_))
245 if(def.argNames.isNil) { ^this };
248 "#{ arg " ++ " " ++ def.argumentString(true) ++ "; "
249 ++ "[ " ++ def.argumentString(false) ++ " ].flop };"
253 // attach the function to a specific environment
255 envir ?? { envir = currentEnvironment };
256 ^{ |... args| envir.use({ this.valueArray(args) }) }
260 Thunk : AbstractFunction {
261 // a thunk is an unevaluated value.
262 // it gets evaluated once and then always returns that value.
263 // also known as a "promise" in Scheme.
264 // thunks have no arguments.
268 ^super.newCopyArgs(function)
271 ^value ?? { value = function.value; function = nil; value }
273 valueArray { ^this.value }
274 valueEnvir { ^this.value }
275 valueArrayEnvir { ^this.value }
282 ugens = ugens ?? { IdentityDictionary.new };
283 res = ugens.at(UGen.buildSynthDef);
284 if(res.isNil) { res = super.value; ugens.put(UGen.buildSynthDef, res) };