In the last couple of years there seems to be an increasing interest (or hype) regarding run-time 'reflection' and/or run-time 'introspection'. (Are they the same thing?). I've been googling for the terms and reading, essentially randomly chosen articles and snippets, and opinion seems to range from it being the best thing since sliced bread to the most evil coding technique yet devised.
Update:corrected typos. Including the one stvn pointed out.
There seems to be a lot of discussion of what it can do--in terms of "you can determine the type of something at runtime"--but little on why you would want or need to? And why it must be deferred to run-time?
I'm having trouble seeing past the Ruby/Objective-C think that says: We can do it, and not many can, so let's make much of it, as a desirable or even necessary feature.
Premise: There's nothing that can be done with run-time introspection that cannot be done (better) by compile-time decision taking.
Counter arguments?
Preferably of the form: I use it to do X, because it's easier/quicker/cleaner/safer/more intuative/more maintainable than the other (please specify) approach I though about/read about/tried.
Pointers to existing discussion (of concrete uses) would also be appreciated. Thanks.
|
---|
Replies are listed 'Best First'. | |
---|---|
Re: Runtime introspection: What good is it?
by stvn (Monsignor) on Jul 06, 2008 at 04:05 UTC | |
In the last coule of years there seems to be an increasing interest (or hype) regarding run-time 'relection' and/or run-time 'introspection'. (Are they the same thing?). I think you mean "reflection" and yes, they are basically the same thing. Introspection tends to be more about "reading" and reflection tends to include "writing" or runtime generation of "things", but that is not a strict definition in any way. There's nothing that can be done with run-time introspection that cannot be done (better) by compile-time decision taking. First of all, this is silly, it is simply not that black and white a problem. Some problems can be solved very elegantly in dynamic languages like Perl/Python/Ruby, but are painful or just impossible in a stricter language like Java/Haskell/etc. and vice-versa. There seems to be a lot of discussion of what it can do--in terms of "you can determine the type of something at runtime"--but little on why you would want or need to? And why it must be deferred to run-time? Lets take a real world, highly useful usage of runtime introspection, a basic clone function (incomplete and simplified of course). Every single one of those calls to ref and to can is runtime type introspection. Could you defer that to compile-time? Sure, but only by adding more facilities to the language to support that. Here is a Perl6-ish version of the above code using multi-methods: Now the compiler could aggressively compile your program to the point whereby it could find every point at which clone was called and look at what $value contains and "unroll" that code therefore making all the decisions at compile time. But that is a lot of work and a lot of static analysis on the compilers part, and that kind of analysis only works if the language your analyzing has a strong theoretical foundation. For instance, with the above multi-method code, in order for that to work, all those types must be part of the same type "set", and in order for the compiler to actually generate efficient code you must provide a branch for each member of that set, which I clearly am not. Maybe my compiler could infer those missing conditions? Well thats nice assuming it got them right, but how can i be sure? If you have ever really tried to hack with Haskell or Ocaml you will know exactly what I am talking about. Okay, so my point here is that you have a waterbed. If you push it down on one side (moving the runtime checks to compile time), it pushes up on the other side (now your compiler is much more complex and you have a type system to fight with). There is no one right solution to this problem, it is balance that has to be struck by the compiler writer/language designer as to where they want their complexity to be.
-stvn
| [reply] [d/l] [select] |
by gaal (Parson) on Jul 10, 2008 at 15:55 UTC | |
| [reply] |
| |
Re: Runtime introspection: What good is it?
by Joost (Canon) on Jul 06, 2008 at 13:13 UTC | |
Premise: There's nothing that can be done with run-time introspection that cannot be done (better) by compile-time decision taking. Except that compile-time decision taking can only be done at compile time. And many of the languages that that have no or only limited introspection also don't have a compiler available at run time. Once your framework becomes generic enough, it can become too constricting to force a static interface on its components. And the static interface may even turn out to be slower than the one that uses introspection and related techniques. Consider, for example, an OO-relational mapping layer where the fields are all accessible via methods (or public properties, if you must):
In many languages, this is much faster than the alternative: But when you do need to have generic routines (for instance, to print the contents of some object), you need some functionality like: Otherwise you're stuck with implementing all the "reflection" stuff in the API, which generally means the API will be ugly and slow. Aside: all of this was already well understood and implemented in the 70s with Smalltalk. Why the static OO guys seem to think runtime inspection, "duck typing" and meta programming is something newfangled and scary is beyond me.
| [reply] [d/l] [select] |
by chromatic (Archbishop) on Jul 07, 2008 at 05:49 UTC | |
Why the static OO guys seem to think runtime inspection, "duck typing" and meta programming is something newfangled and scary is beyond me. Java dragged C++ kicking and screaming to the easy half of Lisp, circa 1972. | [reply] |
by Your Mother (Archbishop) on Jul 07, 2008 at 05:53 UTC | |
Now that should someday be a chapter's introductory quote in a programming book yet to be written. | [reply] |
| |
Re: Runtime introspection: What good is it?
by syphilis (Archbishop) on Jul 06, 2008 at 14:00 UTC | |
One of the nice things about being able to "determine the type of something at runtime" is that it enables one to change the "type" on the fly - which is precisely what perl does wrt numification of strings, where a "string" (PV) type is changed to IV, UV or NV as needed. Regarding perl, I've also found that being able to determine the "type of something at runtime" has ramifications for operator overloading. That is, operator overloading often benefits from being able to distinguish between object, PV, IV, UV and IV. (It may, in some cases, even *rely* on being able to make that distinction.) Whether these are *good* things is not something I want to argue about - though obviously there's plenty of support for this behaviour ... or it wouldn't exist to begin with. (I should also add that I'm not really familiar with "reflection" and "introspection". Apologies if I've missed the mark.) Cheers, Rob | [reply] |
by BrowserUk (Patriarch) on Jul 06, 2008 at 17:10 UTC | |
Whilst selecting codepath depending upon the (sub)type of a scalar at runtime is definitely a form of introspection, the fact that it is under compiler/interpreter control, rather than programmer control, makes it a somewhat different animal to the normal. Indeed, I would say that run-time dispatching upon the base type (HASH/ARRAY/SCALAR/CODE etc.) of a reference is likewise, something of a 'special case'. The types of reflection that I'm more intrigued by the need for, are those provided by the use of UNIVERSAL::ISA and UNIVERSAL::can and similar. These seemed to be used to provide for 'generic programming' ala C++-style templating solutions. In simplistic terms, as a substitute for providing essentially copy&paste dedicated methods, or resorting to MI and/or deep inheritance trees. An alternative to introspection for dynamic languages is compile-time code generation. Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
| [reply] |
by stvn (Monsignor) on Jul 06, 2008 at 18:14 UTC | |
Tsk tsk, you can't declare special cases that favor your side of the argument this late in the game. Whilst selecting codepath depending upon the (sub)type of a scalar at runtime is definitely a form of introspection, the fact that it is under compiler/interpreter control, rather than programmer control, makes it a somewhat different animal to the normal First of all, it happens at runtime and it is introspection, so therefore it is runtime introspection. It may seem that the programmer is not explicitly asking for it to be done (as in the my example above with ref/can), but the programmers choice of language features have caused the interpreter to do runtime introspection just the same. Ignorance of the underlying technique used by the language to accomplish the things you ask for does not make it any less what it is, which is runtime introspection. Indeed, I would say that run-time dispatching upon the base type (HASH/ARRAY/SCALAR/CODE etc.) of a reference is likewise, something of a 'special case' Nope, wrong, no special cases allowed, you made a pretty clear statement, lets not muddy it up. Even that aside, lets take the case of OCaml. Ocaml is a very strongly typed language and the OCaml compiler spends quite a lot of time rigorously analyzing the code to make sure it is well typed and optimizing it as much as possible. Why does it spend all this time? Well because once compilation is finished, OCaml discards pretty much all of the type information. Yes, that means ref ocaml_variable == 'ARRAY' is completely impossible to do. So (IMO anyway) it is not fair here to add a special case here, because this is just simply not possible in other languages which don't have runtime introspection available. If this were some kind of universal language feature, then maybe, but it is not. The types of reflection that I'm more intrigued by the need for, are those provided by the use of UNIVERSAL::ISA and UNIVERSAL::can and similar. These seemed to be used to provide for 'generic programming' ala C++-style templating solutions. In simplistic terms, as a substitute for providing essentially copy&paste dedicated methods, or resorting to MI and/or deep inheritance trees. Yes, these things are far more exciting, however I fail to see how they are any different from the clone example I provided above? Sure if everything is an object, then I can use polymorphism instead of manual type introspection to fake polymorphism, but just as I said above, just because the runtime system is doing it for you and you are not explicitly asking for it doesn't make it any less. An alternative to introspection for dynamic languages is compile-time code generation. Well, no, that is not 100% true. Compile-time code generation has it's limits, some things just simply cannot be known at compile time (user input, information from the network, etc), and depending on what you are doing with those things, you can't always generate enough code to handle all those cases. And even if you could, it is likely that runtime introspection would be simpler code and quite possibly more efficient as well. Generating many reams of code to replace a simple bit of introspection seems a silly tradeoff to me. As I said above, the best of both worlds is the ideal. Some things are better expanded at compile time, while others are better introspected at runtime. Anyone who has spent any time writing code in very strongly typed languages like Ada/Haskell/OCaml/etc. will have encountered inflexibility that has caused them to have to write complex code for the compiler that could be solved simply and elegantly at runtime. And anyone who has spent any time writing code in more dynamic languages like Perl/Python/Ruby know that sometimes you have to write silly and inefficient code to do something that the compiler really aught to be able to figure out on it's own.
-stvn
| [reply] [d/l] [select] |
by BrowserUk (Patriarch) on Jul 06, 2008 at 18:22 UTC | |
by stvn (Monsignor) on Jul 06, 2008 at 18:44 UTC | |
| |
Re: Runtime introspection: What good is it?
by moritz (Cardinal) on Jul 06, 2008 at 17:36 UTC | |
Premise: There's nothing that can be done with run-time introspection that cannot be done (better) by compile-time decision taking.
Sometimes your data comes from the outside, (for example through a serialization), and if you don't know its structure in advance, you have three choices:
Number one is unhandy and ugly, and number two basically means building a (primitive) meta-object protocol that allows run time inspection on top of the existing object model. If the object model of your programming language is powerful enough, there's no need for that. You can just use native objects for data with a format that's not known at compile time. Which implies less code to write, specifically one abstraction layer that you don't need anymore. | [reply] [d/l] |
by BrowserUk (Patriarch) on Jul 06, 2008 at 18:14 UTC | |
Viola. No introspection needed. Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
| [reply] [d/l] |
by stvn (Monsignor) on Jul 06, 2008 at 18:39 UTC | |
Sigh. Okay, lets try and type your code, cause if we don't check your type usage at compile time then it could just randomly blow up at runtime, which is not acceptable in most cases (and in worst case could open your code to exploits). So, assuming our language allows it, we can say that your function will return a type "union", meaning a value of either a classA or a classH type. So, now, lets try and use the variable that is returned. So, the $x variable must be typed the same as the return value of the function, so it is again a type union. So now lets use $x. Okay, so thats fine assuming that classA and classH all respond to the same methods right? Nope, it is not. Lets look at the definition for classA::method. Our $x is a type classA | classH not classA, so it does not pass the type constraint for it's own method. This can't be good. But wait, maybe you made classA and classH both derived from the same superclass, lets call it classX. Lets re-type your function now: Does this buy us anything? Nope, failed again, because there is no "something" method in classX, so the compiler generates an error when you try and do this:
Okay, so maybe you don't care about types, does this invalidate my points? Nope, because your code still makes the assumption that classA and classH are 100% interchangeable in all cases, they they can be easily substituted for one another anywhere in your code and Just Work. This is an ideal case, and one that only works in very restricted cases and pretty much in non-trivial programs only. The moment your bring in outside code that knows nothing of the interchangeability of classA and classH you open yourself up for a lot of errors, errors that will almost certainly happen at runtime (remember you gave up compile-time type checking already).
-stvn
| [reply] [d/l] [select] |
| |
by stvn (Monsignor) on Jul 06, 2008 at 20:40 UTC | |
It should be noted that this code: requires runtime introspection to work. Perl does not determine the method being called at compile time, instead it will lookup in the @ISA in a depth-first search to find the package which has a method of the name "method". The -> is an operator, that operator is a function just like any other function either built-in or defined by you. By calling the code of that operator you are explicitly telling Perl to do some runtime introspection to find and execute a method for you. So, hmm, I think maybe that saying: Viola. No introspection needed.is not quite correct.
-stvn
| [reply] [d/l] [select] |
by BrowserUk (Patriarch) on Jul 06, 2008 at 21:05 UTC | |
by stvn (Monsignor) on Jul 07, 2008 at 02:55 UTC | |
| |
by moritz (Cardinal) on Jul 07, 2008 at 15:21 UTC | |
The problem with wrappers is that they aren't very re-usable. In the example above you assumed that the sub what_does_it_return is in your own code base, or easily overridable. That's not necessarily the case. So you have to hope that whoever wrote the module you're using has built proper, reusable and maintainable wrappers. In contrast when you have introspection built into your language's OO model, you can just use that, and it will work fine. Another problem with overloading is that it doesn't scale very well. If you have a million data objects, why should you overload them all if you just want to call a method on one of them? Both of my points have in common that you need to plan in advance. You need to know or guess how the result objects of, for example, a de-serializer will be used. But you can't, because in the general case the one who uses your module is cleverer that you and, and more creative, and most of all he's crazier. He'll get some weird ideas of what to do, and there won't be wrappers in place, so he'll have to resort to something different. So runtime introspection is at once lazy and humble - lazy because you don't try to guess in advance all use cases, and humble because you don't think you can guess them all. That's the reason why Perl 6 has a pluggable object model that allows introspection - if something is missing or gone wrong, it's relatively easy to add, fix or replace afterwards. | [reply] [d/l] |
by stvn (Monsignor) on Jul 07, 2008 at 21:11 UTC | |
by chromatic (Archbishop) on Jul 07, 2008 at 21:23 UTC | |
| |
by BrowserUk (Patriarch) on Jul 08, 2008 at 01:44 UTC | |
Re: Runtime introspection: What good is it?
by Anonymous Monk on Jul 06, 2008 at 02:17 UTC | |
The project I am leading at this point is a combination of c# and java. We use the reflection (or introspection) to make code generic. This saves project effort a big time. You said :"...There's nothing that can be done with run-time introspection that cannot be done (better) by compile-time decision taking...". yes and no, some of the reflection code can be really smart, but when you done it at compile time, it ends up with ... something like a long life of if elsif. The downside I suspect is performance. Still wait to be seen as the project is going. Nowadays you don't care whether it is slow, you care whether it is bearable. | [reply] |
by Plankton (Vicar) on Jun 20, 2009 at 06:01 UTC | |
| [reply] |
by Anonymous Monk on Jun 20, 2009 at 06:22 UTC | |
| [reply] |
Re: Runtime introspection: What good is it?
by Pic (Scribe) on Jul 06, 2008 at 18:21 UTC | |
Premise: There's nothing that can be done with run-time introspection that cannot be done (better) by compile-time decision taking. I believe that JIT is a counter-argument to your premise. It's not strictly relevant to the kind of code you're interested in, but the kind of optimizations JIT does (which are a great performance boon) simply cannot be done compile-time. | [reply] |
by chromatic (Archbishop) on Jul 07, 2008 at 05:59 UTC | |
Sure they can -- just not as well. Okay, you can't use the polymorphic inline cache strategy at compile-time, but you can predict which variant will get called the most and emit instructions to redispatch if necessary. (I don't know of any JITs which recompile the dispatch when the call characteristics change, but I can imagine that it's possible. Factor may; I think I read something about that.) | [reply] |
Re: Runtime introspection: What good is it?
by dragonchild (Archbishop) on Jul 07, 2008 at 02:20 UTC | |
Implementing #1 is easily done with a set of if-statements if you can assume a set of rules known at compile-time. Implementing #2 is easily done with a data-driven set of functions if you can assume a set of rule formats known at compile-time. #3 is the sticky wicket. If you allow for run-time introspection, then you can easily build this using function factories. I would be very interested in hearing a solution that is not implemented on top of some form of run-time introspection. These systems tend to be written in a language that provides run-time introspection (either to the programmer or to the compiler). If they aren't, then the programmers tend to write an interpreter which, within it, provides run-time introspection. I haven't heard of a system that meets all three requirements and doesn't use run-time introspection as a key piece to solve the problem. My criteria for good software: | [reply] |
by BrowserUk (Patriarch) on Jul 07, 2008 at 02:34 UTC | |
Read http://www.cs.lth.se/EDA120/assignment4/parser.pdf or the documentation on parsec and several other Haskell parser writing libraries. It's not the way I'd like to go, but it can and is done. Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
| [reply] |
by dragonchild (Archbishop) on Jul 07, 2008 at 12:59 UTC | |
My criteria for good software: | [reply] |
by BrowserUk (Patriarch) on Jul 07, 2008 at 14:59 UTC | |
by dragonchild (Archbishop) on Jul 08, 2008 at 00:54 UTC | |
| |
Re: Runtime introspection: What good is it?
by philcrow (Priest) on Jul 07, 2008 at 19:27 UTC | |
I'd be happy to hear of a generic parsing method for this type of introspection, as it could make my code more efficient. But I think giving up introspection would require giving up genericity. Phil
The Gantry Web Framework Book is now available.
| [reply] |
by BrowserUk (Patriarch) on Jul 08, 2008 at 00:00 UTC | |
First. Thanks for being the first to post a real application. I going to assume that what you are doing is passing the blessed handles to live (populated) instance of hash(or array)-based objects to yaml, and having it return a string that contains: In essence, exactly the same as passing a hash to Data::Dumper, Data::Dump, Data::Dumper::Serial. This is a convenient side-effect of using Perl's hashes as the basis of OO. And it's not something that I would want to give up. Indeed, it's one of two primary reasons for my eshewing InsideOut implementations. Forcing the user to hack the source on those rare occasions when it it necessary to look inside an object simply are not worth the loss of convenience, all the extra work or the abysmal performance. But, it is only a convenience. It is perfectly possible to serialise (say) an inside-out object, or as I used once for a application that needed millions of instances and I needed to save space, a blessd scalar containing a packed string of the instance data. You simply have to provide a toString() and fromString(), or STORABLE_freeze() STORABLE_thaw() methods in each of your classes. Extra work, but perfectly doable. With things like Moose and Class::Std (if your that way inclined), or many of the other OO frameworks, these can (or could) even be generated for you from the Class definition. So, whilst convenient, useful and very usable, this use case doesn't contradict my premise. Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
| [reply] [d/l] [select] |
by philcrow (Priest) on Jul 08, 2008 at 15:16 UTC | |
My definition of introspection or reflection, which I take as synonyms, is anything a Java programmer would need the reflection package for. Perl provides many different syntactic ways to reach the same effects, I call all of those reflection. Thus, in my book, using a string to bless an object into someone else's class is reflection. The most popular reflection users these days are then Object Relational Mappers (ORMs). They need to manufacture classes at run time, for this they need the language's reflection system. Phil
The Gantry Web Framework Book is now available.
| [reply] |
by BrowserUk (Patriarch) on Jul 08, 2008 at 16:16 UTC | |
by rhesa (Vicar) on Jul 08, 2008 at 20:11 UTC | |
| |
Re: Runtime introspection: What good is it?
by sgifford (Prior) on Jul 09, 2008 at 04:06 UTC | |
| [reply] [d/l] [select] |
by BrowserUk (Patriarch) on Jul 09, 2008 at 19:40 UTC | |
This got rather long. Testimony to the thoughtfulness of your use cases; thank you again. Here are some of the things I've used run-time reflection of some kind for, in various languages: (My) conclusionsI hope that if anyone read this far, they will find some of this as useful as I have. Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
| [reply] [d/l] [select] |
by sgifford (Prior) on Jul 10, 2008 at 02:55 UTC | |
Glad to be able to make you think a bit! Let me try to respond to a few of your comments. But first, we should be clearer about our definitions of reflection. I'm including in my definition finding the type of an object (using Java.Lang.Class in Java or typeof in C++ or ref in Perl), and testing whether an object inherits from another object (using instanceof in Java or dynamic_cast in C++ or isa in Perl. If you don't consider these reflection, and are only thinking of getting the methods and member variables of a class, some of my examples don't really use reflection. You will have to know what type of shape is is, in order to instantiate the objectRight, this part doesn't use reflection. For your intersection problem, any language that supports method overloading, C++, Java etc., will allow you to code methods within your Rect subclass with signatures of:This is called "dynamic dispatch" IIRC and the behavior I would like and expect, but unfortunately not the behavior exhibited by either Java or C++. Both determine which overload of a function/method to call at compile-time, and if all you know about the object is that it's a Shape* it will always call the overload for that type. For example, in C++:So that invoking someRect->intersect( someShape ); will get to the right code without introspection. outputs:
To make it work, we have to use runtime reflection: outputs:
This is why the distinction between knowing the type at compile-time versus runtime is important; if the compiler knows the type it can call the correct overload, otherwise it will not. For the "make sure it was a type I could deal with" part of the equation, if all plug-ins are derived from a base class, then the only check required is to verify that the class loaded is derived from that base class.Right, but IIRC the class loading code returns a Class and you have to use runtime type checking to determine if it is the right sort of class. Once the class is loaded, try instantiating a (minimal) instance, and exercising the required methods. Catch and report any errors. You can even check that the loaded class methods return sensible values--And that's something that no amount of reflection can do. All your validation is performed immediately after loading.While this is possible, it is error-prone, and violates the concept of putting things in exactly one place. If I add a new method to my class, I have to remember to go add a check for that method to all places where it is loaded dynamically. If it is loaded dynamically from 3rd party code, I have to notify those parties to check for this new method. The maintenance cost is much higher than the runtime cost of doing this check, IMHO. I think what you are saying here is that you can use reflection to construct a workaround to the quirk, not detect the need for itI'm actually simply saying to detect a need for it, by looking at the type of the object. the classic solution to this is to construct a subclass that inherits from the quirky class and override the troublesome methodsEasier said than done if your code is a library being used by other programs who are creating the object in question and passing it in. All users of your code would have to switch to your subclass, then switch back when the bug in the original code is fixed, which is a maintenance nightmare. As far as the cost of keeping type information and reflection information around, I'm not quite sure what the cost is in different languages. I think it's quite low for C++ RTTI, and I think Perl has to keep that information around anyways, so it's also quite low. But I haven't really seen reflection overused; it seems to be just inconvenient enough that it doesn't get used unless there is a genuine need. | [reply] [d/l] [select] |
by BrowserUk (Patriarch) on Jul 10, 2008 at 06:39 UTC | |
by sgifford (Prior) on Jul 10, 2008 at 08:00 UTC | |
| |
Re: Runtime introspection: What good is it?
by tilly (Archbishop) on Jul 11, 2008 at 17:47 UTC | |
Secondly there are often conflicts between different forms of run-time dynamic stuff. For instance see Why breaking can() is acceptable where I try to explain the conflicts between how Perl defines UNIVERSAL::can and AUTOLOAD. So you can't really use the dynamic code introspection in Perl unless you know that other techniques are not being used, or at least have an idea how they might impact you. Thirdly I'm going to dispute that it is better to do things at compile time than at run time. And the reason why is that at run time you have more information than you do at compile time. For example you have information about what code paths you will and will not execute, and so don't waste time dealing with what you don't need. Yes, I am talking about JIT, but JIT goes a lot further than most people realize. Dynamic Languages Strike Back has a lot to say on this topic that you might like. In particular if you combine introspection with aggressive JITing, you get the opportunity to achieve more aggressive optimizations than you could afford to do at compile time. Why? Well the problem is that at compile time there is no end to the number of combinations you might have to worry about, and if you try to optimize all of them you wind up with an extremely large executable that hits performance problems because it takes up too much memory. But when you go JIT you can see the 2-4 combinations that really get used and optimize those. Of course using that as an argument for using introspection in Perl is seriously disingenuous since Perl 5 does not do JIT and is unlikely to ever do JIT. :-) Now where have I, personally, done stuff at runtime using things like introspection and reflection? Truthfully, not often. But when I've done it, it has been useful. For example in one place in my current reporting framework I have a way that objects from lots of modules can be passed into a particular method in another module. There are several useful methods that they might implement. When I load the first module I don't know what others might exist and I don't know what will be passed in, so I leave the decision as to whether to call the method to run time where I check that the method exists by calling can, and then do one thing if it does and another if it doesn't. Were there other ways to accomplish the same thing? Of course. But it seemed to me that the best way to do that was at run time since at compile time I simply did not have sufficient information to know what might be passed in. In another language at compile time there would have been more information and it could have been done then. However it is in the nature of the beast that this method is only called once per program run. Unless you want to add a separate compile phase (which introduces its own overheads and problems), doing this at compile time instead of run time gains you nothing and would require more overhead. So it stands as a counterexample to your thesis that it is always better to do things at compile time. | [reply] |
by BrowserUk (Patriarch) on Jul 12, 2008 at 08:53 UTC | |
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
| [reply] [d/l] [select] |
by tilly (Archbishop) on Jul 12, 2008 at 15:35 UTC | |
A runtime type check followed by a runtime branching operation is exactly the kind of code that JIT can optimize away if you have a good JIT system. However I reiterate that JIT is a red herring in the case of languages like Perl that don't have it. Going to your exception solution, that solution has a major drawback. There are lots of possible reasons why there could be an exception, and your code has swallowed most of them. Easily fixable, granted, but not without adding more code and obscuring what is going on. And it is easy for a programmer to forget that they need to do that - I've seen many forget exactly that, including you just now. Not to mention the fact that if Perl made a minor change to its error message, then your code would break. Not that Perl is likely to do that, but they haven't promised they won't, and they have documented how UNIVERSAL::can works. Furthermore your criticisms strike me as unrealistic. If I define a plugin API, I expect to have things passed into it that are designed to be plugins. Yes, it is possible (but unlikely if you use descriptive method names, which I try to) that some random module might implement methods named the same as what I expect in my plugins. But if so then it still doesn't matter because no sane programmer is going to be passing it into my module as a plugin. (I can't solve the problem of insane programmers, and I refuse to try.) Thus trying to use something that isn't a plugin as a plugin is not a problem that I'm going to waste code protecting against. Now we have the problem of dealing with a badly designed plugin that doesn't do what it is supposed to do. Before you even consider doing that, you need to understand your problem domain. My problem domain is that I am writing plugins for use in my own module. If the plugin doesn't do what it is supposed to, that is a bug that I will fix. There is, therefore, no need for me to protect against that case. The same would apply for many of us. A problem domain that more closely mirrors what you're saying is one where you're writing a popular application which random third parties will add plugins to. But even there you can defend the position that it is the responsibility of the plugin author to make sure they follow your API, and not yours to code against the possibility that they didn't. | [reply] |
by BrowserUk (Patriarch) on Jul 12, 2008 at 17:13 UTC | |
by tilly (Archbishop) on Jul 14, 2008 at 18:20 UTC | |
| |
Re: Runtime introspection: What good is it?
by Anonymous Monk on Jul 07, 2008 at 01:58 UTC | |
| [reply] |
by BrowserUk (Patriarch) on Jul 07, 2008 at 02:28 UTC | |
True. It was called Fortran IV (amongst other names). It was very simple to learn, very fast to compile and produced blindingly fast execution. It also had functions and even "exception handling", and allowed you to write well abstracted and well structured code. It's problem was that it didn't enforce those. So, without mind numbing discipline, it became too easy to write complex, unweildy, tangled spagetti like balls of string. Even with good discipline, each programmer tended to code each of those missing control structures in different ways. Even the simplest if X then Y else Z endif structure became:
Nest a few of those and see what I mean about spagetti. Another term was double negative coding. With each programmer reinventing each of the common control structures in their own coding style, over and over again, maintenance became a nightmare. Throw in a few bugs, the inevitable design changes and programmer turnover, and you can quickly see how things evolve. Add to that static memory allocation and common blocks, and the need for something better is obvious. ... That's a counter argument. Sadly missing in this thread. Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
| [reply] [d/l] [select] |
Re: Runtime introspection: What good is it?
by sundialsvc4 (Abbot) on Jul 08, 2008 at 18:52 UTC | |
This is one of those things where, “if you need it, nothing else will do.” “Compile-time determinations” are exactly that: they occur when the source-code is compiled and are forever-after fixed into the resulting executable. This is very efficient (and therefore, very desireable), if you know that the thing you are dealing-with truly will not change. But if there is a possibility that the thing is external to you, and therefore “beyond your control or at-least your timetable,” compile-time binding is fairly useless. | [reply] |
by BrowserUk (Patriarch) on Jul 08, 2008 at 19:31 UTC | |
Compile-time determinations” are exactly that: they occur when the source-code is compiled and are forever-after fixed into the resulting executable. You're ignoring the flexibility of what constistutes "compile-time" in dynamic languages like Perl. See also the subthread starting at Re: Runtime introspection: What good is it? which discusses how compiled to binary (static) languages achieve dynamic language-like flexibility through the use of parsers without giving up their compile-time type correctness, or succumbing to building a run-time eval capability. I must admit, I'm surprised to find you, as an "planning is paramount" advocate, coming down on the side of making ad-hoc codepath descisions at run-time :) Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
| [reply] |
Re: Runtime introspection: What good is it?
by gaal (Parson) on Jul 10, 2008 at 15:49 UTC | |
| [reply] |