Pirates vs. Ninjas – Using AppleScript as an Object-Oriented Language

The most important things that are missing for an OOP language in AS is:

  1. Not everything is an Object.
  2. There is no such thing as late bindings
  3. You’re not able to write a class (as a result subclassing is not possible). Script objects can give you an OOP feel but they’re objects (a data collection) and not classes.
  4. Interfaces
  5. Abstract classes
  6. Delegates, prototyping or Function pointers are missing

Great example of how to use the load script example (reminds me of writing AS-Studio code). The drawbacks in these days was a missing constructor, which misses in ObjC as well. You had to agree, with yourself, to use a consistent handler to be called after loading. Then, how do you handle requirements? For instance I have an shell object, mysql object and a regex object. I want the shell object to be a singleton but both mysql and regex needs to use the shell object and are instantiated. My solution to all of this, when I wrote AppleScript Studio, was loading all script objects at launch and define them as globals. Well everything is a singleton and every singleton have some class like features. To instantiate the singleton returns a small object with the singleton as it’s parent Read this and you can see that Shane’s solution is similar to a singleton while mine instantiates, both have their pro’s and cons.

Hey, thanks for looking over RJLoader. I hope it provides some value to AppleScripters working on larger projects. I wanted to go over a few points you mentioned.

  1. I work in Objective-C and use primitive values such as int or bool (non-objects) but, nobody would say Obj-C is ‘kinda’ OOP. Everything does not have to be an object to make it an OOP language.
  2. Late bindings are a nice feature, but they are not OOP.
  3. Totally don’t understand point 3. Classes are frameworks for creating objects. I can (and do) subclass using AppleScript. Examples aplenty in Pirates vs. Ninjas on www.robotjackalope.com
  4. Once again, a nice feature, but interfaces are not OOP
  5. Abstraction. Nice, but not OOP. Unless we’re talking about the concept, which of course AS does very well.
  6. Delegates, prototyping. Nice, but, once again, not OOP. Same with function pointers (where your method return value is pushed directly into another method). However, we do pass pointers of objects into functions/methods as demonstrated in Pirates vs Ninjas. That’s not too shabby.

OOP has a few key concepts and AppleScript does all of them, as far as I can tell.

Inheritance : Ability to get other class features
Polymorphism :Acquiring more than one form
Data Hiding : Hide complexity and gives necessary details.
Encapsulation : Hide complex part of program
Overloading : Can use object with all arithematic operator.
Reusability : One class can be use again and again

The last part, reusability, was greatly reduced without a class-loader. However, with RJLoader, we can enjoy that functionality as well.

Overloading is definitly not possible in AS. Overloading would be possible if I have two identical functions/methods but only differs by the number of arguments. Encapsulation and data hiding is also not possible. Properties and handlers are always publics but can’t be protected or private. Polymorphism can only be applied on classes and not on objects, there is no such thing as own written classes, they are already objects at compile time.

According to your list the only things who remains are inheritance and re-usability (which is in this context the same). A programming language that only give the ability for inheritance isn’t worth carrying the OOP label.

Great that you’ve written Objective-C but what should that tell me? I write PHP, Objective-C, C# and C++ and are recognized as OOP languages (also a lot scripting and procedural languages as well). When I write AS i keep constantly hitting boundaries which problems I don’t have in real OOPs. If delegates, late bindings and interfaces are not possible how can you send messages from one object to another? that is for me a very important feature of an OOP langauge. When you can’t write classes how can I use polymorphism (a real sub type).

Then it’s not only my opinion but also every source (wiki, books etc) says it is not a OOP. Even the AppleScript designers says it’s a scripting language with the ability of calling remote procedures (Apple Events), so I suggest to read this 64 pages PDF and you’ll know how AS actually works and why it’s not OOP. So It’s not my 10 years of AppleScript experience that I feel this way, it’s more than that.

Whoops! You’re right!

I was thinking method overriding which AppleScript does very well. Method overloading is exactly as you describe but, once again, it is not an OOP concept. Simply a programming language feature.

I’ll get back to you on your other comments but, very shortly, these additional features you mention are not strictly OOP, but rather implementations that help make OOP more usable.

Cheers. Looking forward to writing a fuller reply.

Those definitions for OOP are a close match to what I was taught in college, but it’s fair to say that some researchers view any attempt to distill OOP to a minimal set of features as futile. However, there are a few features which Benjamin C. Pierce seems to believe is core and AppleScript has 4 out of 5 of these core principles.

Dynamic dispatch “ NO
Encapsulation - YES
Subtype polymorphism - YES
Object inheritance - YES
Open recursion - YES

http://en.wikipedia.org/wiki/Object-oriented_programming

However, if you’re asking how these objects are supposed to communicate to each other – well, that’s a whole different ball of wax. Having programmed Obj-C, Java and quite a few other languages (for longer than I feel comfortable saying), I do understand the need for inter-object communication. However, under the strictest definition of OOP, inter-object communication isn’t a part of the equation. In other words, an OPP language without inter-object communication is still an OOP language. However, if that is the only thing missing from AppleScript, I’ll bet I could figure something out…

As to the engineers, I don’t personally know anyone who was on the AppleScript dev team, but I’ll take your word for it. However, Apple Inc. says it’s OOP.

And, there you have it. It has 4 out of 5 of the major principles of OOP as defined by Benjamin C. Pierce. Apple Inc says it’s OOP. It walks, and quack like an OOP. It’s an OOP.

That being said, nobody would disagree that there are certain features OOP must have to be used in a practical way. For instance, I thought class loading is pretty important. As to the rest of the features you mentioned, they sure would be nice to have, though I don’t believe it’s a deal-breaker.

Still, you got me thinking…

Well William Cook, in his 2006 paper on AppleScript, does say:

IMO, the real question is how useful the limited abilities are in practice. And I still keep coming to the same conclusion, not much.

It’s the same paper Shane :D. I interpreted that as when you say ‘It’s a simple form of’ that it’s actually not.

The TS keep saying that polymorphism (subtyping) is possible which is not. Also keeps saying that encapsulation is possible, which is a form of data hiding, and we agreed that it isn’t possible in AS. The only OO features AS support is Inheritance (using overriding) and open recursion are the only features left again. No offense but you keep listing features where half of the features misses in AS and need complex workarounds to simulate similar behavior.

Well, as long if I’m not able to create classes I don’t consider AS as an Object Oriented langauge. Subtyping is not possible and is needed for an Object Oriented programming language. If you want to give it a name it is close to Object Based languages but IMO it’s definitely not OO.

The features I named is not individually needed because there are alternatives. But when AS misses those alternatives as well I consider this as a drawback. For instance data hiding is not really necessary but when it lacks it should have encapsulation as an alternative which also misses.

I stick with my statement for a while that AS is not OOP.

I’ll happily let you two argue over the theory :wink: I just question the practicality…

You’re right Shane. Who is right or not doesn’t change the fact that there are some missing features. So for practicality this discussion is useless and I’m aware of that. When the theory on MacScripter is correct, it will leave some confusion in practicality for other readers.

I think you’ll actually have to look at at Pirates v Ninjas at www.robotjackalope.com before we engage in any discussion on OOP and AppleScript. You assert that ‘AS doesn’t support polymorphism’, yet I have several examples of classic subtype polymorphism’ which leads me to believe you may be speaking from experience rather than the proof I just gave.

The practicality of OOP AppleScript is in its code re-usability.

Let’s forget RJPirates inheriting from RJPeople and such. Let’s talk real world.

You work with InDesign. A Lot. You’ve created a reliable class which handles just about everything you need handled. Methods like makeNewPageAtEnd, insertPictureAtLastPage, makeBugRegistrationMarksForBleed(arg), etc…

Now, your company has bought the latest greatest InDesign for department A but department B still has the older version - and some of your methods are broken in the latest version. And you have to support both departments.

What to do?

If you used OOP, you’d simply create a new object named RJInDesign13.scpt with a parent property of RJInDesign12.scpt and fix only those 1 or 2 methods which are broken. Within those methods, you would check the version of InDesign and choose to handle using the override method or simply pass the args to the parent.

Now that’s pretty good. That’s classic OOP.

As to inter-object communication, it seems that is possible as well! Only yesterday I discovered a method to have an object send information back to it’s main script, much like delegate methods in Obj-C.

IMHO, I believe there is a lot of OOP functions hidden within AppleScript but without class-loading I don’t believe we had the time or energy to dig into them.

Alright guys. Not only have I argued, referenced and researched, I’ve also written extensively and given code examples as proof of the OOP features available to you in AS. Why don’t ya’ll give it a try?

I do.

And there’s the problem – the stuff is just too varied. Yes, you could make a collection of some common handlers, but you can also do that with copy and paste, or simple libraries. And when it comes to debugging, you don’t get the problem of script objects being essentially black boxes – you can still use Script Debugger and step through.

I understand the value of OOP in places like Objective-C where there are multiple classes, etc, but within the generally procedural synchronous world of AppleScript, I think the advantages get outweighed by the disadvantages. But that’s just a personal opinion/preference.

Like I said, been there done that, have looked into your code but didn’t see anything special, new or ground breaking. I can see that you even haven’t considered testing your code or looked at my latest post (which Shane and I referred to) because you would have noticed that you made a mistake. I won’t tell it yet but the answer is somewhere inside that topic :D.

When I think of subtyping/polymorphism I think that a single interface can be presenting different classes. You considered interfaces more like a fancy feature but is needed for polymorphism. Your example uses method overriding and inheritance and not polymorphism, just like my and shane’s examples.

I’m not saying AS doesn’t have any OOP features because it has open recursion, inheritance, method overriding and property overriding. But these OOP features doesn’t make it a complete OOP language to me and doesn’t match most documentations about OOP. It’s like you’re saying something with 4 wheels, axles and chassis is a car (it could be a trailer, plane, quad etc…). You’re correct about some rejections of features that are needed in OOP like data hiding for instance. When data hiding is missing there should be an alternative, encapsulation for instance, but AS misses that as well.

In C I can use function pointers and structs in a way that it feels very OOP, but that doesn’t make the language OOP it remains just procedural.

I don’t have time to participate in this, so I just let my code speak for it self.

Polymorphism is fully possible, as I can continue a handler, that then are executed from the parent object. The two objects below, where the queue inherits from the stack, can be used side by side in a script.

Classes in Objective-C are also objects. And it is not hard to write a dispatcher, should you need it.


property stacklib : me
script Stack
	property parent : AppleScript
	property __stack : missing value
	on init()
		set my __stack to {}
		return me
	end init
	on Push(athing)
		set my __stack to {athing} & my __stack
	end Push
	
	on Pop()
		local athing
		set athing to first item of my __stack
		if athing is missing value then
			return null
		else
			set my __stack to rest of my __stack
		end if
		return athing
	end Pop
	
	on Peek() # For debugging
		local athing
		set athing to item 1 of my __stack
		if athing is missing value then
			return null
		else
			return athing
		end if
	end Peek
	
	to makeStack()
		script Stack
			property parent : stacklib's Stack # For getting at the parent
			property __stack : missing value # For a unique stack!
		end script
		return Stack's init() # My instance.
	end makeStack
end script

property queuelib : me
-- property Stack : Stack of (load script ("Path to my scriptlibraries containing the :Stack.scpt" as alias))
script queue
	property parent : AppleScript
	property Stack : Stack of (load script ("Path to my scriptlibraries" as alias))
	# has to be put inside to encapsulate it from the namespace
	
	to makeQueue()
		script theQueue
			property parent : Stack
			# Has to instantatiate an object in order to inherit.
			property __stack : {} # For a unique stack!
			to dequeue()
				local athing
				set athing to end of __stack
				if athing is {} then return null
				try
					set my __stack to items 1 thru -2 of my __stack
				on error
					set my __stack to {}
				end try
				return athing
			end dequeue
			to Pop()
				return null
			end Pop
		end script
		
	end makeQueue
	
end script

The script that uses both a queue and a stack


property toplevel : me
property mainProperty : 3
property Stack : Stack of (load script ("Path to hfsfolderwith the :Stack.scpt" as alias))
property Queue : Queue of (load script ("hfspath to folder with :queue.scpt" as alias))
script inheritTest
	on run
		local _stack, _queue
		
		set _stack to toplevel's Stack's makeStack()
		set _queue to toplevel's queue's makequeue()
		
		
		repeat with i from 1 to 5
			_stack's Push(i)
			_queue's Push(i)
		end repeat
		
		repeat with i from 1 to 5
			log _stack's Pop()
			log _queue's dequeue()
		end repeat
	end run
	
end script
tell inheritTest to run

Then don’t post some code that isn’t polymorphism but method overriding! Again you haven’t seen my post either because it’s the same.

Again, that’s not polymorphism that’s open recursion in inheritance.

Since when? There is definitively a difference between classes and objects in Objective-C.

Dispatchers are part of oop’s implicit runtime who enables polymorphism and late bindings. Then you need to write an OOP runtime it doesn’t make your language OOP but you’re creating a new superset, AS++ ;P.

set x to a & b

Is handled by a dispatcher, at run time it looks what types variable a and b are and call the right code. If both are string objects then the string concatenation function is called, when they are both lists or records they are merged and other objects are inserted in a new list. How would you implement this in AS with script objects without creating a runtime (a superset)?

I tried to find it in another document from apple, but it was kind of hard, since “object”, Objective-C" and “class” permeates the Cocoa Fundamentals guide. Here is however another link: Cocoa with Love: What is a meta-class in Objective-C? (Search for “itself”).

As for polymorphism, if we are to be that strict, and follow the definition, then I guess recursive inhertance won’t cut it. (I have never heard that term before.) now, say I stuffed the stack with objects that contained a printme method each and every one of them. And then had a print method in the stack that called the printme method of the object, then I guess polymorphism would be satisfied.

It is totally contrived, and nothing you would do in a stack object really, since it is a collection object. But since recursive inhertance obviously works, then your polymorphism, wich by definition is to be able to work on objects on different types, will also work. Polymorphism in object-oriented programming - Wikipedia, the free encyclopedia

My own conclusion about the OOP qualities of AppleScript, is that it support its mechanisms, but it is really a kluge. But so much less of a kluge than say Java, or C++ for that instance, though multiple inheritance isn’t possible here.

As you see by my code I can’t follow the parent chain, or I can, but that will eventually lead to table overflow when the script grows larger.

There is also no way to override things like the copy command.

Operator overloading has nothing to do with OOP really, though that kind of syntactic sugar are nice to have.

I know how Objective-C works, my question is do you? Then we’re not talking about overloading and I never mentioned recursive inheritance. Open recursive is just me which is equivalent to this and self in other languages to be able to access methods and properties withing the object even if it’s called from outside the object. Inheritance is achieved by an hierarchical singly linked list of objects which has to be followed by the runtime. Therefore I say that AppleScript does include these features.

I’ll keep saying that there are no classes:

script a
	property p : missing value
end script
class of a

Every script object is class script and when it becomes a type it would return ‘a’. there is no type definition so there can’t be type depended method call and therefore polymorphism is not possible. Now it’s just inheritance, when the method is missing in the object go to it’s parent until you’re in the root object and return an error. An good example of polymorphism (an that Objective-C uses hidden interfaces) is that when a method needs an NSResponder that I can send an NSButton to it without problem. This decision is made at run time based on types. Because all script objects are the same type the is no decision and therefore no polymorphism.


script animal
	on talk()
		return missing value
	end talk
end script

script dog
	property parent : animal
	on talk()
		return "Woof"
	end talk
end script
   
script fish
	property parent : animal
end script

fish's talk()
set dog's talk to bark
on bark()
	return "WOOOOF!!!"
end bark
dog's talk()

Animal, dog an fish are objects but not types or classes. The class, type, of every object is script and therefore there is no differences between object types. Also using method overriding it proofs that the script is an object of class script and not an actual class on it’s own. So if the class/type of every script object is identical what would polymorphism do in the code above? Excactly, nothing because there is no polymorphism.

Thanks DJ! Please repost the code you and Shane worked on. I’ll take a very close look. Much appreciated!

Thsi a recent post And read post 1, 5 and 7 in that order and you notice that you’ve been using singletons in your Pirates vs. Ninja examples and not instances. You can also use copy command to create instances BTW.

I view your talk method, as an informally defined interface, both your dog, and your fish adheres to that, and that is polymporphism good enough for me.

Your definition of polymorphism, is somewhat different than the one taught in school, and by Wikipedia.
That is my conclusion.

Copying objects is not something you would do to create an instance of an object in applescript, as you then perform a deep copy, copying the whole context of the running script with it. not only what is inside it, but also what surrounds it.

That is why I have declared the parent as AppleScript, to avoid that, as the code is meant to work.

I also assign new instances to local variables, and not “free” or implicitly global variables in the run handler, to avoid creating “closures” that effectively locks down the instance with the initial value.

There is at least possible to create object oriented collections in AppleScript, and create working object hierachies, without too much fuzz. But there are stuff like above to take into account when you want your code to actually work.

I felt I had to write another piece of toycode, just to illustrate polymorphism, how I understand it.
as for the variable number of arguments methods can take, you can just pass with it a record.
then methods will always adhere, the return value, is also “free” to be anything.

script anAsClass
	property parent : AppleScript
	property myclass : "rootClass"
	to isa()
		return my myclass
	end isa
end script
script vehicle
	property parent : anAsClass
	property myclass : "vehicle"
	
	to _move for adirection
		log " moving in " & adirection
	end _move
end script


script boat
	property parent : anAsClass
	property myclass : "boat"
	
	to _move for adirection
		log "sailing... towards " & adirection
	end _move
end script

script sailboat
	property parent : boat
end script

script train
	property parent : vehicle
	to _move for adirection
		log "hauling towards " & adirection
	end _move
end script


to makeMoving for anObject by adirection
	
	tell anObject to _move for adirection
end makeMoving

makeMoving for train by "left"
--> *hauling towards left*

makeMoving for sailboat by "starboard"
--> sailing... towards starboard
log sailboat's isa()
--> boat 

Now this doesn’t exactly conform to the usual class operator of as, but it suffices.