"Can’t set item 3 of listItems of «script AssociativeList»..."

Breaking objects on the S & R… picking up from where I was last time.

After correcting some proofreading errors in the printed version I’m now stuck on a script object that has one nonworking handler.
Same code implemented as ‘standard’ handlers works.
This is the handler version:

property friendsAges : {{theKey:"Jan", theValue:27}, {theKey:"Sam", theValue:29}, {theKey:"Bob", theValue:35}}

on findRecordForKey(theKey)
	repeat with refRecord in friendsAges
		if theKey of refRecord = theKey then return refRecord
	end repeat
	return missing value
end findRecordForKey

on deleteItem(theKey)
	set refRecord to findRecordForKey(theKey)
	if refRecord is missing value then
		error "The key wasn't found." number -1728 from theKey
	end if
	set contents of refRecord to missing value
	set friendsAges to every record of friendsAges
	return
end deleteItem

findRecordForKey("Bob") --> a pointer

deleteItem("Bob") -- no problem

friendsAges --> no Bob

And here’s the script object - same code, but deletion fails:

on makeAssociativeList()
	script AssociativeList
		property class : "associative list"
		property listItems : {}
		
		on findRecordForKey(theKey)
			repeat with refRecord in my listItems
				if theKey of refRecord = theKey then return refRecord
			end repeat
			return missing value
		end findRecordForKey
		
		on setItem(theKey, theValue)
			set refRecord to findRecordForKey(theKey)
			if refRecord = missing value then
				set end of my listItems to {theKey:theKey, theValue:theValue}
			else
				set theValue of refRecord to theValue
			end if
			return
		end setItem
		
		on deleteItem(theKey)
			set refRecord to findRecordForKey(theKey)
			if refRecord is missing value then
				error "The key wasn't found." number -1728 from theKey
			end if
			set contents of refRecord to missing value --> ERROR
			set my listItems to every record of my listItems
			return
		end deleteItem
	end script
end makeAssociativeList

set friendsAges to makeAssociativeList() -- create an object

tell friendsAges -- set some values
	setItem("Jan", 27)
	setItem("Sam", 29)
	setItem("Bob", 35)
end tell

listItems of friendsAges --> same as before

deleteItem("Bob") of friendsAges
--> "Can't set item 3 of listItems of «script AssociativeList» to missing value." number -10006

The code is exactly as in the book, I only changed variable names to camel case.
I’ve left out handlers that the examples don’t use.

What’s wrong? Did I miss another proofreading error?

Well not exactly the same. As you copy the script in your 1st example into a script object and tell that script object to run it won’t give you any problems. So there must be a difference, and there is. When you delete an item you get out of scope because you use my listitems in delete item and also you call the local handler and then you use in a repeat loop my again. Remove the my in

repeat with refRecord in my listItems 

and your script works fine.

I did no such thing. That object is straight from the book’s sample code, with only the changes I mentioned.

Thank you, that did it. I’ll have to study a bit more. (I’m sure this won’t be the last error to trip me. Seems publisher allowed two nickels on proofreading, hmm?)

I couldn’t understand DJ’s explanation and, I must admit, I’m still perplexed even after playing around with the script for myself!

The error message Alastor933 was getting was “Can’t set item 3 of listItems of «script AssociativeList» to missing value.” ‘AssociativeList’ is of course a variable local to the handler makeAssociativeList() ” and yet the script object is «script AssociativeList» even outside the handler, where the variable no longer exists!

set friendsAges to makeAssociativeList() -- create an object
return friendsAges --> «script AssociativeList»

However, that doesn’t seem to be the problem. Leaving the ‘my’ in the repeat line gives a returned ‘refRecord’ value of .

. instead of .

Both references can be used in every valid way except except that the first one’s contents can’t be set, for some reason. On the other hand, if the repeat line is changed to .

repeat with refRecord in (a reference to my listItems)

. the returned reference looks exactly the same as the first one, but this time its contents can be set and the script works without erroring! Confusing or what?

Personally, I feel that since the handlers in the script object all access the list directly anyway, it would be better to pass an index around rather than a reference. This would make the code easier to read, would avoid the vagaries of stored references, and might even work slightly faster. I’d also give the handler parameters different labels from those of the record properties! (Or possibly vice versa.) This sort of overloading’s dreadful:

set theValue of refRecord to theValue

Thus:

on makeAssociativeList()
	script AssociativeList
		property class : "associative list"
		property listItems : {}
		
		on findRecordForKey(givenKey)
			repeat with listPosition from 1 to (count listItems)
				if (theKey of item listPosition of my listItems = givenKey) then return listPosition
			end repeat
			return missing value
		end findRecordForKey
		
		on setItem(givenKey, givenValue)
			set listPosition to findRecordForKey(givenKey)
			if (listPosition is missing value) then
				set end of my listItems to {theKey:givenKey, theValue:givenValue}
			else
				set theValue of item listPosition of my listItems to givenValue
			end if
			return
		end setItem
		
		on deleteItem(givenKey)
			set listPosition to findRecordForKey(givenKey)
			if (listPosition is missing value) then
				error "The key wasn't found." number -1728 from givenKey
			end if
			set item listPosition of my listItems to missing value
			set listItems to every record of my listItems
			return
		end deleteItem
	end script
end makeAssociativeList

set friendsAges to makeAssociativeList() -- create an object

tell friendsAges -- set some values
	setItem("Jan", 27)
	setItem("Sam", 29)
	setItem("Bob", 35)
end tell

listItems of friendsAges --> same as before

deleteItem("Bob") of friendsAges
listItems of friendsAges

Obviously this doesn’t help if you’re trying to follow the examples in a book. :wink:

I had started, but not finished, trying that. Got confused, and asked…

Yes, that tripped me a few times while making the little changes. Will do this next.

It does help. I’'ll be just a bit better prepared for what might be wrong with the next one…

FWIW, I found that all the my-ing is not necessary. The script works fine without them.

When it get too detailed I get problems writing thing down properly because English is not my native language, and I’m sorry for that. Well I will try it with an example. Alastor fault was simply this


property theList : {{theKey:"Jan", theValue:27}, {theKey:"Sam", theValue:29}, {theKey:"Bob", theValue:35}}

set refRecord to a reference to item 3 of my theList
set contents of refRecord to missing value --gets an error

return theList

What happens is that I’ve used my double, as we know local code is like there is a big ‘tell me’ block around it. So referring to my the second time is getting out of the object and refer to it’s property and not it’s object that it’s holding. So when getting the property it can return the reference and eventually the value but when I try to use contents of, it still try to refer to the property and not it’s values. So getting the values doesn’t give you any problems, changing it does…

Then how about Nigels example? well this is what he actually does is creating a pointer to a pointer.

property theList : {{theKey:"Jan", theValue:27}, {theKey:"Sam", theValue:29}, {theKey:"Bob", theValue:35}}

set refRecord to a reference to item 3 of (a reference to my theList)
set contents of refRecord to missing value

return theList

Why does this work? Anyone familiar with pointers to pointers? Now refRecord points to item 3 of a pointer to the property, still not it’s object. But because it points in two steps it is possible. Step 1) the pointer to the property and returns a pointer to it’s object, step 2) use the newly created pointer in step 1 with the second pointer and point to the object.

p.s. sometimes the result in the script window can mislead you. Sometimes you see the object while the actual variable doesn’t hold a object or better said points to the object.

In general you can say that in 99% of all cases that my before properties is only needed when it is not me .

Yes. I tried getting a grip on things with ‘log this_or_that’. Confused me.

That made me smile.

Sorry, DJ. I didn’t mean to insult your English. I just couldn’t grasp what was happening with the references in the script. I think I now see a glimmer of light, thanks to your second explanation. :slight_smile:

Yes it does. But if you’re talking about my version of the script, it’ll work much faster with them if the list is very long.

I don’t feel insulted at all, no harm feelings here. When it gets too detailed or complex I have often problems expressing myself in foreign languages also when I’m at clients or speaking with other developers on the phone as well. You can’t be good in everything right?