Delegates vs Functions

Sora574
Hey, I have a question...
What's the real difference between delegates and functions?
I mean, as far as I can tell, you run delegates the exact same way as functions, except they're attached to objects...

To me, this seems really pointless. Instead of
RunDelegateFunction (ball, "throw", param1, param2, param3)

Why not just use this?
ThrowBall (param1, param2, param3)

There must be something I'm missing here...

jaynabonne
I view it as:

- You use functions when you always want the same functionality. You are always invoking the same piece of script.
- You use script or delegate attributes when you have functionality that could vary per object. Having a script or delegate attribute makes it an indirect reference - you know you're going to invoke *something* attached to the object, but you don't know what it is. It all depends on what has been set, and what has been set can change dynamicaly.

Now, in the realm of scripts and delegates, things are a bit ugly (in my opinion), but I view that as the evolution of Quest and (sadly) not going to change.

First, you have scripts, which are invoked (optionally) with a dictionary of parameters and can't return a value. I tend to use scripts for simple things, ideally in cases where I have no parameters or return value! :) It's just too awkward for the caller to have to go through the rigamarole of setting up the params for each call. I *will* use them with parameters for internal functions, where the pain of creating the parameter dictionary and populating is hidden below the covers. But I would not want to use them, with parameters, for a public interface. For that, I'd define a delegate.

Moving on to delegates, you have to actually define the signature for it, so that it knows what the parameters mean. And you define the return value type as well. (An aside: I have come to be a bit confused about why functions and delegates need return types at all. Quest is so open-ended about types, that you'd think it would be fine just returning *anything* and not care. And a forced type means you couldn't, for example, implement something like GetAttribute, which can return different types from the same bit of code.)

Having defined a delegate, though, invoking them is not too bad - with one caveat. If you want to invoke a delegate without a return value, you use "rundelegate". If you want to get a return value back, you have to use "RunDelegateFunction". I haven't tried just always using RunDelegateFunction and ignoring the return value. Perhaps that works. It's one of those areas I've learned about the hard way (by trying to get a return value out of rundelegate) and haven't explored it fully. I think it has to do with the expression parser and what it can operate on. (For example, you can "eval" a function that returns a value, but you can't "eval" something like "msg" which doesn't.)

Ok, probably more than you wanted, but in case it's useful to anyone... :)

Alex
jaynabonne wrote:(An aside: I have come to be a bit confused about why functions and delegates need return types at all. Quest is so open-ended about types, that you'd think it would be fine just returning *anything* and not care. And a forced type means you couldn't, for example, implement something like GetAttribute, which can return different types from the same bit of code.)


It's an unfortunate implementation detail really - these functions are handled by Quest code, and FLEE needs to know what type they will return before it can compile the expression which will then evaluate them.

jaynabonne
That makes sense. Thanks, Alex!

Sora574
Wait...
So, technically, couldn't you just use a function with switches in it?
This would get the same results as a delegate, right? You could still include parameters and return values...

homeeman
Delegates are also useful in that you can use the "this" pointer, which acts as a variable of type "object" that refers to the object the delegate is an attribute of.

So if I was cloning an object because players can generate as much of it as they want, or because it is generated randomly, I might use a delegate instead of a regular function because it is much simpler to use a delegate rather than a function with an extra parameter.
You can also include delegates in a type, which is useful for a lot of the same reasons.

jaynabonne
Sora, for some specific stuations, that would work, and you could do that, especially if it's just something quick and dirty (aka, you can't be bothered to do it properly), you only have a few cases, and all the cases are known. But *even then*, I would still do it such that the functionality is grouped with the object (just as variables are) instead of spreading the logic around in the code.

I had a long piece of text written in response to this (which I started and deleted multiple times), and we can discuss further if you want and I could get into it, but before we get into "why modularization is good for your code", I thought I'd root out your interest. :) (Remember that this is all a bit abstract - with writing code, as with many things in life, just because you can do something doesn't mean it's aesthetically pleasing or "correct" in the grander scheme of things... so it comes down to how much you care, really, in the end).

Basically, it's a choice between grouping your code up based on objects vs grouping your code based on operation.

Let's say you have four doors (A-D) and two operations, open and close. You need to write code to handle the product of that (open A, close A, open B, close B, etc). You can either group it by "open" and "close", where you have a single function that handles all the "open" cases for all objects, etc - or you can group by object, where you put all the code for each object with that object. It ends up being the same code; it's just a question of how it's organized.

Programming design (and, let me tell you, my own experience) says that it makes more sense, makes better code, and makes a happier programmer if you have each object manage its own affairs rather than grouping things by operation. In other words, it's better to have each object decide how to open and close itself rather than have monolithic "opener" and "closer" functions that need to know about all relevant objects. For one thing, it makes adding and removing objects much easier (consider touching one place - the object - rather than all the scattered places where the individual pieces are). And if you want a library that works with arbitrary objects (e.g. something like Quest), then you simply can't do it with a switch, as it means that rather than just adding new objects and having the code talk to them, the new pieces of logic would need to go into your library, adding your object handling into all these various places, and the idea of a library is that you shouldn't have to do that.

The rule of thumb I have arrived at after (*cough* too many) years programming is this: if you want to operate uniformly across a range of objects, then if you have to know what type an object is when you're using it, something is wrong. It's one of those big red flags. The idea is: you shouldn't have to know what type an object is - you just need to know how to talk to it. The object should manage its own affairs. The object sets the policy. The object holds the implementation. If you want to know how an object's function is implemented, you look in the object, not in some function grouping all these various implementations into one place.

Another thought: since you're basically organizing little snippets of code (open A, close A, open B, etc), by having functions with switches, you're actually writing more code, since rather than just invoking the function on the object, you actually have to have all this logic (the switch) to route the request to the right code. It's like, instead of handing a message to your co-worker sitting next to you for him to handle, you take and hand it to someone else (e.g. the "open function with switch) who reads it *on behalf of your co-worker*. And if you had a different message or request, you'd hand it to someone else again (e.g. the "close function with switch) who would, again, handle it for your co-worker. That's not exactly parallel, but somehow feels relevant.


That's probably a bunch of babble, and I apologize. I hope some of it makes sense. :)

jaynabonne
Of course, as soon as I submitted and walked away, the proper term hit me: "coupling". As much as you can keep your code "loosely coupled" (in other words, the less one piece of code has to know about another), the more general the code can be and the more applicable it can be to more situations.

A switch statement based on (for example) object id would be tightly coupled. You'd have a single function that would bring together into one place handling for all these different objects instances. It binds together knowledge of all these disparate objects in one place. Basically, it's just not good. :)

Edit: Here's an article that might be interesting: http://msdn.microsoft.com/en-us/magazine/cc947917.aspx

Sora574
Hmm... That actually makes a lot of sense.
That article helped too, although I didn't understand any of the code in there. If I ever get around to making more than just the start screen for the game I'm making, I'll try to follow those guidelines.

Now... How the heck do I implement delegates?
As far as this goes, it seems to be poorly documented...
I found this, but that says absolutely nothing about object side stuff. The links it provided don't say much either, except for the fact that delegates can be called with 'rundelegate' or 'RunDelegateFunction' pointing toward object scripts (I think it's supposed to be a script? It says "Now you can simply use the delegate name as an attribute type name" which doesn't make much sense...)

jaynabonne
Here's an example:

<delegate name="GetCombatTextType" parameters="target, hits" type="string"/>

This defines the delegate signature, what parameters it takes (if any) and the return type (if any), just like a function.

In an object, you'd then have:

<object name="Creature_Gremlin">
<alias>gremlin<alias>
<GetText type="GetCombatTextType">
return ("The " + this.alias + " hits " + target + " with its sword, for " + hits + " damage.")
</GetText>
</object>

Note the parallel with a script - it's just a different type (your delegate type instead of "script").

And then you can call it with:

s = RunDelegateFunction(creature, "GetText", "you", 3)
msg(s)


Hope that helps!

Sora574
Ohhh that helps...
What's up with the 'properties' part of the wiki code then?

jaynabonne
Could you give a link? I'm not sure what you're referring to. :)

Sora574
Sorry, here:
http://quest5.net/wiki/Delegate_element

jaynabonne
I see. Yeah, it looks like a mistake. I can't figure out any way that "properties" would make sense there.

Sora574
Hmm... Okay, well thank you for the answers!

This topic is now closed. Topics are closed after 14 days of inactivity.

Support

Forums