David Haley said: An internal 'cleanup' might or might not be needed for implementation reasons, but these are precisely implementation details and the user should never, ever have to worry about cleaning up implementation-specific temporaries.
As soon as I 'access' data or 'request' a function's return value, I have the values in hand and they should go away immediately in any other form they might exist in other than the values I have.
You misunderstand me, these ARE implementation details. The user only has to deal with PPI.Load, PPI.Expose, and whatever methods the service exposes. I explained them because it's critical to understand how PPI actually works, in the context of this discussion.
David Haley said:
Quote: with slightly more complex serialization to accomodate PPI-specific requirements
It's still a little unclear to me, to be honest, if these requirements were driven by actual problems to solve, or problems that could be solved.
I mentioned function callbacks, which have a very clear use that I've mentioned and utilized already. If there's a specific other requirement I mentioned that you don't understand the need for, I'd be glad to address it directly.
David Haley said:
Quote: but there is no difference between a table containing a table, and a table containing a reference to another table.
Actually, there sort of is. You can think of it as the difference between a containment tree and a containment graph. I think we all agree that we want to be able to pass tables with subtables; it's still unclear to me (and Nick, I believe) why we want tables that can refer to themselves (which is what arbitrary table references allow).
I'm still not understanding, sorry... If you have a table, and you serialize subtables (something we agree we need), we don't want to serialize the same subtable multiple times if it appears more than once (i.e. a graph rather than a tree, as you said). This requires a cache mechanism, which I use. At this point, tables that refer to themselves - or simply cyclic references in general - are valid simply because of the cache. I certainly wasn't thinking of self-referential tables when I wrote the code.
David Haley said:
Quote: but I found it was easiest to roll my own
A general rule of thumb is to avoid rolling your own unless you really, really have to.
For whatever it's worth, your serialization code (as posted on page 2, v1.0.1, was the most recent I saw in the thread) does not allow a table key to be anything but a string or number, whereas Nick's can (with some limitations). Before you point at the limitations, I would continue to argue that limitations are only a problem insofar as they prevent the tasks we actually have from being accomplished. To be fair, table keys of type 'table' are perhaps weird in the first place, but part of your argument is being language-independent and as general as possible. So this could be a source of confusion. In fact, your serialization function silently ignores stuff it can't serialize, which could lead to very confusing problems!
I hate to say it, but you're several versions behind. I began pasting them elsewhere and linking to them after they got too large for the posts. The latest version is here:
http://mushclient.pastebin.com/f16a36fbc
In relation to things that can't be serialized, my version uses z:~ in its place, which is just a serialized nil.
David Haley said: TBH, and I hate to harp on this, but it still seems to me like the requirements haven't really been defined. I'm not talking about implementation requirements, I'm talking about API requirements. We're talking of "PPI requirements" but I haven't seen a single place where the high-level goals are cleanly laid out, with real-world use cases and justifications for each of the requirements.
I think in a very, um... different manner than most people I've met. It's very hard for me to lay things out so concretely. I tend to work by examples, such as my ATCP plugin - I posted the new version a day or two ago in its own thread. Most examples are also in my head as I work (and I do throw out ideas that simply aren't useful). If you have any concrete questions about the API, rather than asking me to lay it out for you, I would be happy to oblige. >_>
David Haley said: Complexity in general, solving problems simply because you can, is not always a good thing. This is a Very Important Concept and perhaps hard to explain succinctly. There's a good quote about perfection by Antoine de Saint-Exupery; paraphrasing, the designer knows that he has achieved perfection not when there is nothing left to add but when there is nothing left to take away. This is extraordinarily relevant to computer science in general and API/protocol/etc. design in particular. Solving all kinds of problems sounds awfully nice, until you try to solve so many that you end up confusing the user when it comes to solving the problems you initially had. This is a somewhat lengthy reference, but I would recommend this document:
http://cacm.acm.org/magazines/2009/5/24646-api-design-matters/fulltext
(Caveat: I've only read about half of it so far, myself)
When I look at PPI, I believe that everything has a very important use case. It all works through the same code system. If I had to, I could limit PPI.Expose to methods again, but I don't see the point: it all works through the same core system. PPI.Expose is like passing a parameter; using a client PPI to access it is like getting the parameter from a function call. It doesn't make sense, to me, to limit it in such a way. It would be adding to the code's burden, rather than removing from it.
David Haley said: To summarize: I find it very difficult to comment without having a very clear picture of the exact problem we're solving and why it's a problem. This should be general, but more specific than just "sending stuff between plugins" -- that much is obvious enough. ;-)
Like I said earlier, it's kind of hard to speak concretely about it unless I have an applicable question. >_> I know what I expect of PPI, but it's hard for me to communicate it.
David Haley said: Here's an example: we need to send functions around. Do we really need to send functions that don't have proper names? If we only send properly named functions, then we can dramatically simplify the passing-around of functions; namely, you specify them by name and you're done.
Sorry, what? It doesn't matter if it has a name or not, you're giving the function value itself to PPI. And it does give it a name, or more properly, an identifier. The identifier could have been a string, but numbers lent themselves better to uniqueness: just increment the last one. Strings just didn't seem to fit anyways.
David Haley said: Yes, yes, it would be nice to construct arbitrary callbacks in Lua using closures etc. and then be able to pass these to VBscript or whatever. But this introduces complexity for the entire library, for a very specific use case -- one that could, incidentally, be solved by the user by giving names to these lambdas in the first place. Yes, this punishes the user who has this (IMHO rather funky requirement), but the alternative is to punish everybody by making a generally more complex system.
Functions are not actually passed between plugins. Identifiers are. Any language could theoretically save this identifier somewhere, and when the user is ready, send that identifier back across with a REQUEST message. |