Scripting Cocoa with F-Script
Pages: 1, 2
The currency converter script, version 2
Below is the second version of the currency converter. This script is shorter because we use some neat F-Script features that we avoided in the first version for simplicity's sake. We also removed comments (the code speaks for itself) and reorganized the program a little bit. The interface is now entirely constructed and configured before being put onscreen. Finally, we added support for the Cocoa memory management system to ensure that the various Cocoa objects we create will be destroyed when no longer in use.
F-SCRIPT CURRENCY CONVERTER (version 2)
window := NSWindow alloc initWithContentRect:(125<>513 extent:400<>200)
styleMask:NSTitledWindowMask + NSClosableWindowMask
+ NSMiniaturizableWindowMask + NSResizableWindowMask
backing:NSBackingStoreBuffered
defer:NO.
conversionScript := [(form cellAtIndex:2) setStringValue:(form cellAtIndex:0) floatValue
* (form cellAtIndex:1) floatValue. form selectTextAtIndex:0].
form := (NSForm alloc initWithFrame:(60<>90 extent:320<>85)) autorelease.
form addEntry:@{'Exchange Rate per $1','Dollars to Convert','Amount in Other Currency'}.
form setAutosizesCells:YES;
setTarget:conversionScript; setAction:#value.
button := (NSButton alloc initWithFrame:(250<>20 extent:90<>30)) autorelease.
button setBezelStyle:NSRoundedBezelStyle;
setTitle:'Convert'; setTarget:conversionScript; setAction:#value.
line := (NSBox alloc initWithFrame:(15<>70 extent:370<>2)) autorelease.
window contentView addSubview:@{form, button, line}.
window setTitle:'Currency Converter'; orderFront:nil.
One of the new things we use in this version is the ; notation for cascading messages. This notation enables us to send several messages to a single receiver without having to re-specify the receiver each time.
Another interesting thing is the instruction:
form addEntry:@{'Exchange Rate per $1','Dollars to Convert','Amount in Other Currency'}
One of the innovative features of F-Script is that it allows us to manipulate entire groups of objects at once, even with methods that have not been specifically designed to support objects collections (actually, F-Script provides a full object-query language, directly usable on Cocoa objects).
This is the case in this instruction where we add a whole list of entries to the form at once. We use the "message pattern" notation (denoted by @) that allows us to specify potentially complex groups of message sends. A message pattern generally involves single or multiple collections of objects: In our example, we use an array of string objects, denoted by { and }. At runtime, the instruction will trigger the generation of these three message sends:
form addEntry:'Exchange Rate per $1'
form addEntry:'Dollars to Convert'
form addEntry:'Amount in Other Currency'
The same pattern is also used in the instruction "window contentView addSubview:@{form, button, line}" where we put a whole set of views into the window at once.
In these examples, we use a relatively simple pattern. F-Script provides a complete syntax that makes it possible to express a broad range of message patterns. All the specific concepts of F-Script, like message patterns, can be used when scripting Cocoa.
The currency converter script, version 3
|
| |
As it stands, our script is not very modular: It's just a set of instructions, using global variables, that we paste into the F-Script console in order to execute them. The following version introduces modularity:
- Our script will become an object itself.
- It will now use local variables instead of global variables, so that we won't pollute the top-level environment.
- It will take the window's title as the argument.
F-SCRIPT CURRENCY CONVERTER (version 3)
converter := [:title | |window conversionScript form button line|
window := NSWindow alloc initWithContentRect:(125<>513 extent:400<>200)
styleMask:NSTitledWindowMask + NSClosableWindowMask
+ NSMiniaturizableWindowMask + NSResizableWindowMask
backing:NSBackingStoreBuffered
defer:NO.
conversionScript := [(form cellAtIndex:2) setStringValue:(form cellAtIndex:0)
floatValue * (form cellAtIndex:1) floatValue. form selectTextAtIndex:0].
form := (NSForm alloc initWithFrame:(60<>90 extent:320<>85)) autorelease.
form addEntry:@{'Exchange Rate per $1', 'Dollars to Convert', 'Amount in Other Currency'}.
form setAutosizesCells:YES; setTarget:conversionScript; setAction:#value.
button := (NSButton alloc initWithFrame:(250<>20 extent:90<>30)) autorelease.
button setBezelStyle:NSRoundedBezelStyle; setTitle:'Convert';
setTarget:conversionScript; setAction:#value.
line := (NSBox alloc initWithFrame:(15<>70 extent:370<>2)) autorelease.
window contentView addSubview:@{form, button, line}.
window setTitle:title; orderFront:nil.
]
In this version, we put the instructions between [ and ] to make the F-Script interpreter generate a Block object. We also declare, on the first line of the script, the argument and the local variables. Finally, we give the name "converter" to the Block object that represents our script.
We can now invoke our script by sending it a value message without forgetting to provide the required argument. For instance:
converter value:'This converter is nice!'
Each time we invoke it, a new, fully functional currency converter is created and displayed onscreen.
Because our script is now an object of class Block, we can manipulate it like any other object: put it in a collection, pass it as an argument to methods, archive it on disk, and more. There are also several facilities specifically related to Block objects, including a graphical code editor.
Finally, because F-Script comes in the form of a framework ready to be embedded into any Cocoa application, it is easy to place our script in a standard Mac OS X executable.
Final thoughts
We have seen what scripting Cocoa with F-Script involves. Clearly, many subjects have not been tackled in this article, such as Cocoa exception handling, object archiving, Interface Builder integration, usage of custom Objective-C classes, or mapping of non-object types, but you can expect a very high level of integration on these aspects.
Philippe Mougin specializes in object-oriented technologies and enterprise systems. He is the creator of F-Script, an open-source object-oriented scripting language for Cocoa.
Return to the Mac DevCenter.
You must be logged in to the O'Reilly Network to post a talkback.
Showing messages 1 through 7 of 7.
-
fixation then illumination
2001-12-14 22:40:32 psheldon [Reply | View]
-
Re: fixation then illumination
2001-12-16 13:53:46 pmougin [Reply | View]
Thanks for your kind comments. Indeed, F-Script is a framework that one can embed and use from its own components and also a framework into which one can plug and use its own components. This leads to some interesting synergies, for instance the fact that blocks are represented by regular Objective-C objects, and thus can be used from F-Script and from Objective-C in the same way. We'll probably explore this further in a forthcoming column.
Best,
Philippe -
I shall enjoy learning to make building blocks
2001-12-16 22:39:44 psheldon [Reply | View]
Building blocks sort of boot strap my mind or amplify it.
I tried to make subclass of Bezier that behaved like a colored pen, but I don't think I really understood what assignment meant in either C or objective C. Just telling myself you pass the address starting the object didn't help me debug. What I tried to do just didn't work. Mike will get to it when he isn't swamped with finals and maybe see what flawed thinking I entertained. He did an article on memory managment which helped me only start to grasp cocoa scope rules, something with the metaphor of passing on and declaring ownership of a scope by objects. But my own exercise was enough to get me in trouble and maybe next time around I'll get a better feel.
Maybe my confusion indicartes others were confusable as well. My confusion might mean that these concepts must be more clearly illustrated with example for others as well. Maybe you all might decide that hammering away at another guy's old bugs that will leave by themselves with the guy's own increased wisdom is not as cool as going on in a column. At my first aerospace job, my boss reassured me that I might well paperclip questions and move on with the reading and then go back to the paperclip questions to see if they answered themselves or are no longer important. I've told myself I can't understand everything in the universe and I might like to move on to more interesting questions. But there is glory after staring at a brick wall finally getting that insight or 20x20 hindsight. Sometimes I think that's the only real learning that goes on.
Maybe you and Mike will figure from my bug in the colored pen subclass use some theme you both would like to strengthen.
You thanked my kindness. I work on this. I find that, myself having a lot of education and investment in building mental tools, some people tend to debate with me and use me as a judge of their own worthiness and not listen to my story, rarely displaying a sense of my worthiness. This behavior is not all that inspirational to me building a story or mental tools. I try to practice what I wish they would do. Do unto others as I would... With some people it's hard.
I think great ideas happen when people can learn to trust each other. That takes time. Maybe it should take time and that's OK. -
wish I would remember about tabs not showing on publication
2001-12-14 22:45:53 psheldon [Reply | View]
Or could the form entry code Oreilly uses recognize tabs as formatting publication. Right now you have to recognize my paragraphs by indents at the end of the paragraph, not the beginning.
Could the form entry into these thread responses to columns be made more wysiwyg. Eventually, I'll learn to detab in bbedit.
-
Good work
2001-12-12 19:09:48 tomldavis [Reply | View]
F-script interests me because of the array features (I use MATLAB a lot) and because of its integration of Cocoa classes. It seems like an easy way to learn your way around the Cocoa environment, which I'm trying to do.
I do have two questions: How do you draw in a window using NSBezierPath? (My drawings always end up in the fs main window.) And can you execute a text file as a script or block?
Thanks,
Tom -
Good work (better formated answer)
2001-12-13 10:53:56 pmougin [Reply | View]
> How do you draw in a window using NSBezierPath? > (My drawings always end up in the fs main window.)
See the method "lockFocus" in class NSView. It allows designating the context in which drawings are done. In the following example, we open a new window and draw a rectangle inside it, using NSBezierPath:
"Instantiate and configure a window"window := NSWindow alloc initWithContentRect:(100<>400 extent:300<>300)
styleMask:NSTitledWindowMask + NSClosableWindowMask + NSResizableWindowMask
backing:NSBackingStoreBuffered
defer:NO.
"Put the window on screen"
window orderFront:nil.
"Get the content view of the window"
view := window contentView.
"Draw in the context of the view"
view lockFocus.
(NSBezierPath bezierPathWithRect:(50<>50 extent:200<>200)) fill.
view unlockFocus.
"Ensure our drawing is actually displayed on screen"
window flushWindow.
Note that when the system will ask the view to redraw itself (for instance, this will happens if the window is resized), our drawing will disappear. This is because the view doesn't know about it. With the AppKit, the way to view objects redraw themselves is determined by the code in their drawRect: method, not by what has been drawn inside them in the past.
> can you execute a text file as a script or block?
From F-Script, you can load a text file inside a string, build a block from that string and execute the block. For instance, suppose we have a text file at path '/myscript.txt' whose content is an F-Script block literal:
"Loads the text file into a string"
text := NSString stringWithContentsOfFile:'/myscript.txt'.
"Generates a block from the string"
myBlock := sys blockFromString:text.
"Execute the block"
myBlock value.
Of course, you can combine these instructions in one line:
(sys blockFromString: (NSString stringWithContentsOfFile:'/myscript.txt')) value
If your script takes argument, just use the correct value... method.
Best,
Philippe -
Good work
2001-12-13 10:45:40 pmougin [Reply | View]
> How do you draw in a window using NSBezierPath? > (My drawings always end up in the fs main window.)See the method "lockFocus" in class NSView. It allows designating the context in which drawings are done. In the following example, we open a new window and draw a rectangle inside it, using NSBezierPath:"Instantiate and configure a window"window := NSWindow alloc initWithContentRect:(100<>400 extent:300<>300) styleMask:NSTitledWindowMask + NSClosableWindowMask + NSResizableWindowMask backing:NSBackingStoreBuffered defer:NO."Put the window on screen"window orderFront:nil."Get the content view of the window"view := window contentView."Draw in the context of the view"view lockFocus. (NSBezierPath bezierPathWithRect:(50<>50 extent:200<>200)) fill.view unlockFocus."Ensure our drawing is actually displayed on screen"window flushWindow.Note that when the system will ask the view to redraw itself (for instance, this will happens if the window is resized), our drawing will disappear. This is because the view doesn't know about it. With the AppKit, the way to view objects redraw themselves is determined by the code in their drawRect: method, not by what has been drawn inside them in the past.> can you execute a text file as a script or block?From F-Script, you can load a text file inside a string, build a block from that string and execute the block. For instance, suppose we have a text file at path '/myscript.txt' whose content is an F-Script block literal:"Loads the text file into a string"text := NSString stringWithContentsOfFile:'/myscript.txt'."Generates a block from the string"myBlock := sys blockFromString:text."Execute the block" myBlock value. Of course, you can combine these instructions in one line:(sys blockFromString: (NSString stringWithContentsOfFile:'/myscript.txt')) value If your script takes argument, use the correct value... method.
Best,
Philippe






I decided I was feeling crabby because I still had to navigate to the Chistmas Caroling and had to give trying to think positive a rest.
Just now it hit me, after the party and a bit of discussion griping about grippy people, you built a framework. Woooh. That's far out. I think that ranks you as a pedestrian advisor.
I have this holy grail of commenting omnigroup's game frameworks to sort of motivate myself with fun to learn how to navigate knowledge of programming building tools built by mobs of people without having to relive their lives. Finding out how people can be useful to each other in work groups, crucial to postgraduate publication in academia for tenure.
And you built a framework.
I've learned to "think in the mathematical language Maple". One of its features that a person can exploit to "amplify his thinking" is to assign complex expressions rather than their evaluations to variables.
And you made a framework with a block you could assign to a variable that recorded the organization and modularity worked on by a programmer. Woooh.
I saw some portents of stuff going on in parallel in that curly bracket construction of your framework or parser.
Steve Weyer made a framework for the newton message pad in the original apple newton compiler. That meant that he really understood Apple's compiler and what programs it could write. He also made an equivalent of netscape called newtscape. You could embed newtonscript and html in books and run other programs through them. He had books that ran his compiler and made tutorials. He coauthored with his young daughter. He touched the clouds and advised pedestrians.
So, cool. Glad your on board. Maybe AppleScript Studio at the end of this month will be a hard shadow to stand in to make a stand on being the only game in town on scripting, but I think you had to understand stuff better than those nearsighted guys who might see you in some sort of shadow and you have a lot of knowledge for us to mine.
I shall look forward to your column as I do Mike's.