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

I understand where you are going to but the need of adding a property myclass and a method isa (like the C code behind Objective-C) only proves my point that all objects are the same class.

To me, polymorphism is more than two objects responding to a method with the same name. That is for me the only thing that has been proven and tested through the years so far. There is object inheritance but that’s another subject, not polymorphism.

For me polymorphism would look something like this:


script eString
property parent : string
on bsize
return (do shell script "/bin/echo -n " & quoted form of s & " | wc -c") as integer
end bsize
end script

Now I can use estrings and send them to every object that would normally expect an string class. If that would be possible I would say that (subttype) polymorphism is completely supported in AS.

I think we can give it a rest because for me the AS glass filled with OOP is half empty and for other it’s half full.

We can really give it a rest, for me the glass is partly broken, giving all the stuff you have to take into account to make it work properly, it is a hack at best! :slight_smile:

Still, I use the stack, and queue classes I used to illustrate earlier, and when I need a tree in the future, I think I’ll implement that as a “class” as well, or a double ended queue. It has some benefits, with regards to reusability.

However, the objects I like the most, is the script objects. Just tell them to run! -But have them stay put in their products folder, and not in the script menu. They can be told to run from within an app, or an Automator service too. :lol:

I think we are experiencing slight differences in definitions. DJ Bazzie Wazzie, wants to directly extend AppleScripts string object so he can make it do something the original engineers did not create. Since this is not possible, DJ Bazzie Wazzie asserts AppleScript does not support subtype polymorphism and therefore is not an OOP language.

Of course, that is not entirely correct. AS does have subtype polymorphism – as McUsr and myself have demonstrated. However, AppleScript does not allow subtype polymorphism for it’s own objects. That much is true. DJ Bazzi Wazzie leaves the conversation implying we are stuck.

However, we’re not stuck. We have the freedom of creating our own object which inherits from string and creating whatever methods we desire. In fact, I created RJArray which adds methods I wished were present in AppleScripts list object.

But all of this talk about what OOP is (or isn’t) and what features we are missing (or not) has obscured the original point - practically erasing it from memory.

Why OOP? Why does this type of language exist in the first place? What are the advantages?

Code reusability. That’s it. It’s all it does.

It’s fair to say www.robotjackalope.com - Pirates v Ninjas has a target audience in mind for AppleScripters who do not use (or do not understand) OOP concepts and to encourage that audience to look into it – for the sole purpose of code reusability.

If you are an AppleScripter working on large projects, frequently digging into old projects where you solved problem X and copying and pasting into new projects, then AppleScript OOP is made for you. If that doesn’t describe you, don’t OOP. You have no need for OOP. In fact, you probably shouldn’t be looking at this post at all! Leave now and look at pictures of cute kittens! The internet is filled with pictures of cute kittens! However, if you’re still here and not looking at pictures of cute kittens, I happen to think OOP with AppleScript is pretty aweseome.

In the Princess Bride (1987), Wesley answers Prince Humperdincks challenge of a duel to the death with a classic line. “No. To the pain!”. Wesley goes on to describe exactly what he means by this which convinces Prince Humperdinck to drop his sword and surrender.

This line neatly summarizes the battle between procedural and object-oriented programming. When the pain of procedural outweighs the pain of writing OOP, procedural drops it’s sword and surrenders. Despite all the hemming and hawing on AppleScript OOP we’ve had, the OOP features AppleScript does have is more than good enough for the vast majority of AppleScript projects. If they’re not, AppleScript probably isn’t the right language for the task - and that brings us to the real reason for the www.robotjackalope.com - Pirates v Ninjas post.

I wrote this post because if somebody else had written it 15/20 years ago, I believe I could have moved into Java much quicker. I could have written Objective-C much sooner. I could have been a contender!!

My first and longest experience with programming is AppleScript, a language I love to this day. I believe the AppleScript engineers were building the programming language equivalent of Marijuana - The starter drug that will lead you down the road of shooting up the heroin of Objective-C, or Java, or Ruby or whatever…

In some ways, this post is a farewell love letter to AppleScript. It’s my way of sending a message to myself through time, hoping a younger version of me will receive this message in a bottle. I do this because AppleScript is the greatest programming language ever created that nobody was ever willing to pay for.

So, there you have it. AppleScript as OOP. And, you even have a classy class-loader and a dozen or so examples, all there at www.robotjackalope.com. If the ideas take off, I’ll happily help and manage the project on github. Let’s see where it goes!

Great thread everybody. Thanks for weighing in. Your comments were greatly valued!

Hello.

Many small files, makes it inherently hard to debug, especially during the initial runs, so I suggest you use an approach like below, until you know for sure that your object works, or later, when you have to test them, because, the problem with AppleScript, is logging really, when it comes to small files, during runs, you will have no idea about what is happening in your loaded script objects when you call their methods. It goes without saying, that going from having it in a file, and putting it back into another leads to editing of every parent property, at minimum.


property toplevel : me

script class1
	property parent : AppleScript
	property myprop : "class1"
	to talk()
		log "Hello I am " & my myprop
	end talk
	to init()
		script maker
			property parent : toplevel's class1
			property myprop : "an instance of " & my parent's myprop
			-- explain that you have to provide the instance variable
		end script
		return maker
	end init
end script

script class2
	property parent : toplevel's class1's init()
	property myprop : "an inherited class of " & toplevel's class1's myprop
	to talk()
		log "Hello I am " & my myprop
	end talk
	to init()
		script maker
			property parent : toplevel's class2
			property myprop : "an instance of " & my parent's myprop
		end script
	end init
end script


on run
	local a, b -- that the holder of the object must be a local, in order to avoid closures. in the run handler
	set a to class1's init()
	a's talk()
	set b to class2's init()
	b's talk()
end run

And as for your usage of the parent property, I guess that will work allright with tiny scripts, but then again, I guess the whole context of the running environment will be copied with it, as your script object then also has the toplevel script as its context. This may lead to an interal table overflow error. Which is rather nasty if you can’t pinpoint it to an actual cause. And when you do, then you have to remodel your whole object hierarchy.

This is not something I know for sure, as I am not sure if it happens if the parent property is set to something, but I know it for sure that it will happen, if the parent property isn’t set at all. That is however not a risk I am willing to take, hence the way I implemented the stack and the queue above.

You should also mention that the copy operator should be avoided at all costs, and that you’ll have to provide your own clone operation, as the copy operation copies the whole running environment with your object.

Copying during a couple of iterations, where your object is held by a global variable, is guaranteed to make your script crash, do you to memory consumption.

You haven’t mentioned the that you should avoid the usage of undeclared, or global variables to hold your objects, in your run handler, this may not only lead to out of memory problems, but also to closures, which means that your object becomes locked to the initial value it got, and will keep that as it’s value no matter what you do to your object, apart from deallocating it by setting it to missing value.

And finally, I am glad we all could help you, and that you are so enthausiastic, to me, this is a bit like fixing your car with bubble gum and wire.

That is AWESOME feedback McUsr!! I’ll modify accordingly.

I really like the explicit declaration of the parent property to AppleScript. I’ll use that. However, creating a separate property to declare the class name isn’t exactly necessary as we can use the “my name” operator.


on RJPerson()
    
   property parent:AppleScript   

   script RJPerson
 
      on init()
         return me
      end init
 
      on talk()
         return my name & "says, Hello!"
      end talk
 
   end script
 
   return RJPerson's init()
 
end RJPerson

Certainly, creating the classes ‘in-line’, as described in Pirates v Ninjas is the best way to create several objects that must work together. Good call. Once you’re happy with the object(s) it should be a simple enough matter to copy and paste them into their own separate classes.



property dependencies : {"RJPerson.scpt", "RJCowboy.scpt", "RJLoader.scpt"}

on alloc()
	
	script RJCowboy
		
		property parent : (load script alias (POSIX file (do shell script "pathToLocalLibrary=" & ¬
			quoted form of (POSIX path of ((path to me) as string) & ¬
			"Contents/Resources/Scripts/" & (item 1 of dependencies)) & ";" & ¬
			"pathToCommonLibrary=" & quoted form of (POSIX path of ((path to scripts folder) as string) & ¬
			"Contents/Resources/Scripts/" & (item 1 of dependencies)) & ";" & ¬
			"if [ -e  ${pathToLocalLibrary} ];then echo ${pathToLocalLibrary};else echo ${pathToCommonLibrary};fi")))'s load()

		on init()
			set my agility to (my agility) + (random number from 1 to 3)
			set my health to (my health) + (random number from 5 to 8)
			return me
		end init
...


What is this ‘interal table overflow’ you mention?

As the copy, global and such, I suppose I could mention that, but damn, it’s pretty long as it is. I was kinda going for 'everything you need to know about OOP" in as short of space as possible.

Like in every OOP the amount of objects are limited. The limits of AS is very low compared to Objective-C for instance so when you do a deep copy you’ll hit boundaries very early.

Is there a way we can reproduce this in code?

Are you talking about memory management like so…

set person to RJPerson()
person's talk()
set person to null

Hello!

Those links my give you some ideas.

MacScripter / Script has grown too large for editors

How to resolve “internal table overflow” in Applescript - Stack Overflow

MacScripter / Internal table overflow. (error -2707)

Re: AppleScript error, overflow.

MacScripter / Trouble with internal table overflow in Script Debugger 4.5.7

This last one is a very good read if you want to simulate an internal table overflow error, by saying copying some lists between objects, or just have objects with lists. It is a seemingly very precise description of the container datastructures of apple script.

MacTech | The journal of Apple technology.

Oh that is amazing stuff. Strangely, I’ve known about the copy directive for some time but almost never used it.