coding challenge: passing a function as a variable in a 'run script'

Last week I posted an Applescript question about accessing a slot of an object using a variable. Thanks to all for the great suggestions (particularly StevanK’s comments).

This week I’m wanting to know how to pass a user-defined function into a ˜run script’ command, or another way to get analogous functionality.

I know you can pass a parameter (e.g., an object) into a ˜run script’ command like this:
tell application “iTunes”
run script (“on run{cTrack}
set " & slotName & " of cTrack to "” & slotVal & “"
end”) with parameters {cTrack}
end tell

But can a parameter passed (cTrack in this example) be a function? Specifically, I want to run something like:
tell application “iTunes”
run script(“on run{cTrack,cFunction}
set " & slotName & " of cTrack to "” & “cFunction (” & “"” & slotVal & “"” & “)
end”) with parameters{cTrack,cFunction}
end tell

Where ˜cFunion’ is a user-defined function outside of the ‘run script’ command.

I actually haven’t tried that, but my guess is that it won’t work, due to the fact that ˜cFunction’ is not a parameter (variable), it’s a function.

I’m also thinking of nesting the above script command within a tell ’ application “cFunction” ’ command, analogous to how I nested the function within a 'tell application “iTunes” command… this way the script ‘sees’ the function ‘cFunction’ as well… but not sure if this would work either (i.e., can we extend the database of ‘tell application X’ commands by defining our own???)

Any ideas anyone? StefanK (you had some great suggestions last time)?

For lispers, passing a function as a variable to another function is done directly (i.e., functions are treated just like constants and variables in lisp).
In Matlab, these are called ˜function functions’, and are defined by the @ command.

I’m hoping Applescript has something similar.
-Clayton

You could pass the function name as a string, to be used by a Run Script command.
Perhaps something like this.

save this script in the file “FirstLevel”

-- FirstLevel script
on run {ftnToRun, paramA, paramB}
	return run script ftnToRun as alias with parameters {paramA, paramB}
	
end run

,this in a file “mySum”

-- mySum script
on run {a, b}
	return a + b
end run

and this in a file “myProduct”

-- myProduct script
on run {a, b}
	return a * b
end run

Then this “master” script allows you to pass a choice of functions to FirstLevel

set sumFunction to "Macintosh HD:Users:merickson:Desktop:mySum.scpt"

set productFunction to "Macintosh HD:Users:merickson:Desktop:myProduct.scpt"

set chosenFunction to choose from list {sumFunction, productFunction}

display dialog (run script "Macintosh HD:Users:merickson:Desktop:firstLevel.scpt" as alias with parameters {chosenFunction, 3, 2}) as text

Ahhh… so cool!!!

OK. So, let’s look at how I would plug this idea in my particular case.

I want to run something like:
tell application “iTunes”
run script(“on run{cTrack,cFunction}
set " & slotName & " of cTrack to "” & “cFunction (” & “"” & slotVal & “"” & “)
end”) with parameters{cTrack,cFunction}
end tell

But this won’t work quite this way. So, let’s change this to:

set cFunction to “(run script "Macintosh HD:Users:cstanley:desktop:foo.scpt" as alias with parameters {slotVal})”
tell application “iTunes”
run script(“on run{cTrack,slotVal}
set " & slotName & " of cTrack to " & cFunction
end”) with parameters{cTrack,slotVal}
end tell

But this is a bit clunky, due to the fact that I originally had the function ‘foo’ defined in the same *.scpt file as the above code (i.e., it’s simply a subroutine), and I’d have to save it in it’s own *scpt file to get this to work… Any way you can refer to a subroutine in a *.scpt file using this ‘run script x as alias’ solution???

-Clayton

One other way would be to pass a “function code” to the routine and then use branching like

If functionCode = "mySum" Then -- do something ElseIf functionCode = "myProduct" Then -- do something else End if
That would remove the need for seperate files for mySum and myProduct.

Yeah, but then that nests both functions “mySum” and “myProduct” within the ‘do something’ areas, making them inaccessable elsewhere… I’d like to be able to ‘pass’ these functions into a ‘run script’ command so that I don’t have to ‘redefine’ them within this ‘run script’ command, as that would defeat the purpose of passing them into the ‘run script’ command in the first place.

Kind of a circular explaination… but does that make sense?

-Clayton

Maybe script objects could also be a technique to accomplish this
http://developer.apple.com/mac/library/documentation/AppleScript/Conceptual/AppleScriptLangGuide/conceptual/ASLR_script_objects.html

The code for the mySum and myProduct could be stored outside of the If Else structure.

If functionCode = "mySum" Then Set myResult = sumMethod(arguments) ElseIf functionCode = "myProduct" Then Set myResult to productMethod(arguments) End If

Yes, but not outside of the function that the if-else structure resides in. This, is what i’m trying to do; pass a function to another function.

Am I misunderstanding something here?

I envisioned the overall structure that:

Outside, you have the Master script.

CalledRoutine contains the If…Then I showed is inside CalledRoutine.

Master can pass the argument functionCode to CalledRoutine as a string.

“How to pass a function to a sub-routine” is pretty much the same problem as “how can one call a function, given a string”.
Or at least in the approaches that I suggested.

Yeah, we’re on the same page, but this brings me back to my original concern.

I have a master script.

Inside the master script (as a subroutine), I have the function ‘functionCode’. Within the master script, I want to make a ‘run script’ command that passes ‘functionCode’ into it. But, as far as I know, a ‘run script’ is a sandbox, and it doesn’t have access to the current ‘state’ outside of it. Which means that in order for the function to see ‘functionCode’, I have to pass it in as a parameter.

Your first way around this would work. I run a ‘run script functionCode’ inside of the first ‘run script’, but this requires ‘functionCode’ to be defined in its own script file, which is clunky.

That’s as far as I am right now. I can call another script inside of a ‘run script’ command, which essentially allows me to call a function inside of another function. But I have to define the function that I want to call in its own script file, which isn’t ideal…

Sefank, I haven’t looked into your solution yet; maybe tonight,…

-Clayton

Yes, this should work. I just place a subroutine inside of a script object, and then pass that script object as a parameter to a ‘run script’ command.

I’ll try and code this up tonight, get it running, and then post the snippet that works.

-Clayton