in reply to Re^4: replace conditionals with polymorphism
in thread replace conditionals with polymorphism

You are overcomplicating. See Re: replace conditionals with polymorphism for the format of a basic dispatch table. And then when I say "dynamically built" think about having a bunch of code that builds the hash that drives things. Just because it is dynamically built doesn't mean that you have to have offloaded stuff into a configuration file.

For a realistically complex example of what you can do with a dynamically built dispatch table consider Why I like functional programming. Then think about what it would take to rewrite that example in an OO fashion.

  • Comment on Re^5: replace conditionals with polymorphism

Replies are listed 'Best First'.
Re^6: replace conditionals with polymorphism
by ELISHEVA (Prior) on Feb 10, 2009 at 08:16 UTC

    Tilly, that is one beautiful piece of code, but here's the irony - you could have made it even more general purpose by using OOP. Not, of course, by getting rid of the dispatch table (heaven forbid!), but rather by turning the configuration data into the data portion of an object and the definitions of dispatch routines into methods.

    As a result, you would have a framework for multi-syntax parser that could process any mini-language that had those four tokens (open tag, close tag, open constant tag, escape code). Viewed as a whole, your code is a great example of the Type B OOP (multiple related type dependent functions). Furthermore, the idea of using a dispatch table to handle tokens is arguably a form of logical programming. The dispatch table defines a set of "rules". The rules play themselves out until they have nothing left to say.

    Which all goes to the essential point of your post on why you like programming with functions. That point was the quote from Tom Christiansen to the effect that one who isn't comfortable with all the paradigms: imperative, functional, objective and logical is like a chef that knows how to boil, but not bake.

    And bringing things back to video studied by the original poster, your code, OOPified or not, highlights one of the essential problems with that video (and much of OOP education) - the failure to teach which of the many kinds of relationships between data and function, function and function should be encapsulated in an object. Often the obvious ones (choosing which function to process a token) are not the important ones (defining the collection of functions for a mini-language). The fact that you need different functions for different tokens isn't like to change over time. But the definition of the function for each token easily could and probably needs to change in a coordinated manner with other rules for processing tokens.

    Thanks for a good read,

    Best, beth

      I could do that, but that would be premature abstraction.

      The rule of thumb that I try to live by is to not attempt to abstract something until I'm doing it for the third time. Why do I do that?

      Well the first time I guarantee that I don't know how similar examples will vary and won't be able to pick a good abstraction. So attempting to abstract at that point will result in more code that is less to the point (and therefore less clear), but I'm very unlikely to find an abstraction that is useful for my next time. Which means that my abstraction has lots of real costs and no real benefits.

      The second time I now have a point of comparison. I could abstract and it might work. But in my experience I still don't know enough for the cost/benefit to work out. Also at this point I am consciously fighting the second system effect.

      The third time I know enough about the problem and have enough experience that I am confident that I can come up with the right abstractions. Abstractions that pay off enough in future maintenance to justify the cognitive overhead of having to learn them, and the initial effort of writing them.

      Moving back to this specific example, you are perfectly right that if I had several variations of HTML format that I had to deal with, an OO layer would be a great way to go. However if I don't, adding an OO layer would add code and make it more difficult to see what is going on. It would also make other kinds of changes more difficult to make.

      Let's make this concrete. In the several years since I wrote this I have never once wanted to have several similar markup languages that just varied in the syntax of the tag. (Which is what your proposed OO layer would give me.) However I have encountered other needs, such as emoticons and the need to have tags that are only allowed to open when another tag (eg a table tag) is open. Those changes would be complicated by your proposed OO interface since they cut across that interface and require changes to both sides.

        I could do that (no OOP), but that would be premature under-abstraction :-)

        No seriously, you make a great point about your having used this module for years and not needing that particular abstraction. And I know you have loads and loads of experience. But let me give a counter story.

        One of my first jobs out of school was supervising a team trying to put together a consolidated data model and data dictionary for about 300 coorporate information systems. I learned a lot more than I ever wanted to know about the vagaries of abstraction and language. And most of all I came to the conclusion that there will never be one way to do things and that the best one could do to control confusion was to create good frameworks for doing the same thing many ways. (Maybe that is why I like Perl so much?).

        The main reason for OOPing up an implementation is that it provides a framework for TIMTOWTDI. When software is released publicly or across a corporation, it is virtually guarenteed that needs will change across user groups and time. Even during development, customers rarely can articulate or correctly prioritize the corner cases until they've seen a few demos, and by that time, an awful lot of code may be written.

        However, just to be clear, I was not suggesting a full scale OOP evangelist style of OOPing up. Most OOP implementations go way overboard creating a mess of interconnected objects that would make a circuit board engineer's eyes cross. They assume that all objects must be mutable. That danger lurks wherever data is accessible to outsiders, etc, etc. Yada, yada, yada..

        Rather I had in mind something much simpler. Something that would allow for the possibility that an OOPish need will arise without destroying the overall clarity of your code. How would I do that?

        As a first step, I would make changes that do nothing more than make it possible for consumers of the parser to call your parser using an OOP syntax. Internally, aside from the ever-present $self, the object wouldn't really be OOP at all. This would involve little more than

        • adding a dummy new(...) method that did nothing more than bless and return $self.
        • adding $self as a parameter to all of your methods
        • making the wrapper around the dispatcher a method and adding $self as a parameter to the dispatch call

        I'm not even sure I would bother giving new(...) parameters (other than $sClass) or even moving the configuration data into a hash. And though I would likely add getter methods for configuration data, I certainly wouldn't bother adding mutators or fancy methods to hide data.

        My reasons would be the same as yours - we don't know enough about the way it should be abstracted (yet). Maybe the users don't need to configure the object, they only want to override token processing. Maybe they want to configure, but are ok with doing it only when the object is constructed. Maybe they need a fancy way to add new syntax elements or may be they don't. Maybe they are happy to rely on training and social controls to keep data private and maybe they aren't. We just don't know. All of these can be added later without disrupting existing code. So I would keep it simple.

        Ah...but if the changes are so little - why bother? If you really need to, you can do those kind of changes later on. For now they are just fluff...

        Why? Because for very little cost, it reduces (not eliminates) the risk that we will have to go out and review 100K or more lines to find all the places we need to change from a function interface to an OOP interface. It reduces the risk that we will need to release a backwards incompatible product to clients. It reduces the risk that we will have to redesign the class to support both a procedural and OOP interface (which will definitely muck up the clarity of your code). It reduces the risk that we have to choose an awkward ordering of parameters just so we can guess which interface is being used. For very little cost and virtually no premature abstraction we create an interface that is more likely to grow in a managable way.

        So yes, I agree with minimalism, but the minimalism should be in the design of the object, not in the avoidance of objects.

        Best, beth