Condividi tramite


Thoughts on static import

Eric has a post on why he thinks static import is bad and has garnered a lot of feedback so far.  I was reading his post, and while I tend to agree that static import is bad, I do so for some slightly different reasons.  One thing I agree on is that static import makes maintainability very difficult.  He says:

“One can argue that for its intended use - things like Math.Sin(), it will make things clearer, which I agree with. The problem is that I can see this feature being used by people who are familiar with their code to save time, and for them it's obvious that they've done that, but that's not true for the poor guy who inherits the code.”

in the feedback darren says:

Darren says “my test of readability = "how long would it take someone who spoke English, but has never seen a computer in their life, to understand the code"“

Wow, this is a very good case to consider then.  Pretend we allowed static import.  Then someone would have written in the code “Sin(blah)”.  When the next person comes along and sees that they go “Oh dear god!! My code is sinning!! What the heck does that mean!?!!”, *Crosses themselves*, and performs an exorcism.  If it were written Math.Sin(blah), then they could say “hrmm... it's sinning, but I guess that's some weird thing that Math geeks do.  Must be a different time of sinning than I'm used to.”  In this case the forcing of explicitly naming something helps in this case because it means that someone who knows English, but isn't a math/computer geek, can have a better chance understanding the code.  Namespaces and types serve to add information that is lacking in just a simple function name.  English is incredibly vague and ambiguous.  (Hell, just imagine if these paragraphs constituted a program.  Would you really understand what it all meant?).  In order to make things clear we move away form english into a more rigorous system specifically sothat someone can come along and, with a small bit of learning (a key differentiating factor with what darren wants), understand what's happening. 

Darren also mentions that this is a godsend when dealing with situations like adding a specialized function to deal with strings: “For example, I HATE duplicated code. It really offends me to see if (instr(theString, searchString)>-1)... I would love to see if (theString.Contains( searchString)) - but as I have no way of adding Contains to String,“

I agree with him on this.  It's incredibly obnoxious to not be able to add this sort of functionality to the appropriate class thus forcing you to put it on another object.  However, that seems to me to be a consequence of the bad design of some classes in the framework and the inability to extend them usefully.  IMO two wrongs don't' make a right.  We should fix those classes rather than adding features do work around bad designs.  What's worse is that by added these features to the language I feel that people will start making more and more of these badly designed classes expecting people to be able to work around the issues with things like static import.

Darren then bring up two more interesting points: “The more information you hide, the stronger the system. I DON'T WANT people caring about (or knowing?) WHERE the functionality is coming from - I just want them to know it's out there, and to use it.” and “Overloading: Now, if blah happens to start of life as an int - then the function above is fine, and Min is resolving to Math.Min. Suppose we then want to change it to type "ReallyBigInt"... well - Math doesn't define a Min of ReallyBigInt. We always have to define a Min for ReallyBigInt's, but why do we have to also change every function using it? I just want to USE my new ReallyBigMath library, and leave everything else exactly the same“

These are excellent points.  However, again, they speak to the poor design of the current frameworks.  We already have a mechanism for hiding information and implementation details and for dealing with overloading.  We have a fricking OO language here (with lasers!! ) and yet we're not using it appropriately.  Going back to both the Math.Sin and Math.Min examples.  Rather than saying Sin(x) or Min(x, y) we should have done: x.Sin (after all the sine is just a property of a number) and x.Minimum(y).  By having actual methods that operate on the instances you have you get rid of th need for static importing of a type and you can easily replace that type with another type in the future and have all your code still work (ahhh, the joy of interfaces).  Look at StringContains.  I would much rather just have string.Contains instead.  that would allow me to say: if (title.Contains(“Mr.“))  instead of “if (StringContains(title, “Mr.“))“, but maybe that's just me.

IMO, this is one of those features that will just breed more bad code.  :-)

I've heard the argument that we shouldn't limit the power one has just because people can abuse a feature.  However, so far I disagree with that.  Look at the Office APIs.  They're extremely confusing, clunky, and badly designed because they were catering to a language feature that allowed users to abuse things badly.  It's been my experience (especially in the C++ world) that features will get abused, badly.  I think, therefor, that it's a bad idea to introduce a feature that will only be a good thing 1-2% of the time, especially when it makes things such a confusing mess when abused.

Note: This is not because I'm a control freak (ok I am... nO staTic impr0t 4 j00!!!!), it's because I don't see any features in the language as free.  Adding something like this in means now everyone picking up C# 2.0 has something new to learn and understand.  Eric said it quite well with “int x = Process(s);  ...  it's a line of code that's easy to understand, because you know what Process is, and more importantly, you know where to go look for it.”  Now every single C# developer will have to take that rule which they've lived by and understood, throw it out the window and retrain their minds to say “I am now no longer invoking a method in the context of the current type, but in the context of all my usings as well.”   We can't just say “if we add this feature people can ignore it and go about their merry way.”

Also, I find this topic fascinating because I prefer functional langauges where it's quite natural to have functions floating around that are accessible without any of the clutter we are forcing here (specifically being inside a class, being static, and needing full name qualification).  However, I still consider the last point about complexity to be valid here.  While there's something to be said for merging OO and functional principles and designs so that you get the best of both worlds, you also end up with a much more complex system that can be a lot tougher to learn.  I could very well be wrong on this.  However, I have always preferred langauge that kept things simple and kept the number of constructs to learn as small as possible.  There is a great sense of knowing (in those languages) that you really do understand anything that you read.  Contrast that with Perl, which is (For me) still incredibly difficul to read and undertand purely because there are so many constructs (And related interactions) to learn and understand.  This is a source of its great power and flexibility, and probably why people love it so much.  However, it's also a reason that many consider it to be an incredible pain to work with.  Remember that many developers don't work in a vacuum.  They work in pairs or on teams, or they work on code they inherited from someone else.  Keeping complexity down is very helpful for making that a much smoother process.  Note: I am showing my bias here.  Darren said “how long would it take someone who spoke english, but has never seen a computer in their life, to understand the code“.  That's not a principle i live by.  With that kind of philosophy I feel that we would end up with programs that read like the current US tax code :-)    

So, in short, I believe that static import is bad because we're using it to get around badly designed APIs (much like optional parameters), and instead we should be fixing up the APIs.  The more “power” features we add that have the potential for abuse, the more I feel that we'll see badly designed APIs that force us to use those features.  It's a vicious cycle that I don't want to start down!

Comments

  • Anonymous
    July 07, 2004
    The comment has been removed

  • Anonymous
    July 07, 2004
    The idea is not just to throw in all the features that would possibly be useful. If there is any chance of it being misused, why don't you let FxCop do its job of telling what is good use and what is misuse. I really think deciding on usability is not a job for the language but for a reviewer system.

  • Anonymous
    July 07, 2004
    just curious, doesn't have anything to do with static import, but why is this allowed?

    using Mojo = System.Int32;

  • Anonymous
    July 07, 2004
    W.R.T. adding a Sin (or Sine) property to a number, when do you stop? Do you add Tan, Cos, Sec, Cos, Sinh, Cosh, Ln, Log, Lg, arbitrary logarithms, powers, roots, etc? If not, then where do all those functions belong? Should I be able to add my own functionality to double or int? Should I create my own numeric classes with all those functions? Should I derive from double or int (assuming this were possible)?

  • Anonymous
    July 07, 2004
    Brant: You wouldn't have to calculate sine unless it was actually asked for. There would be no perf overhead to this :-)

  • Anonymous
    July 07, 2004
    David: "Do you add Tan, Cos, Sec, Cos, Sinh, Cosh, Ln, Log, Lg, arbitrary logarithms, powers, roots, etc".

    Yes. These are all things you do with numbers and it follows that they should be vailable on any number.

    "Should I be able to add my own functionality to double or int"

    Yes. Inheritance is one of the fundamental parts That's one of the fundamental parts of an OO system.

    "Should I derive from double or int "

    Yes.

  • Anonymous
    July 07, 2004
    David: "Do you add Tan, Cos, Sec, Cos, Sinh, Cosh, Ln, Log, Lg, arbitrary logarithms, powers, roots, etc".

    Yes. These are all things you do with numbers and it follows that they should be vailable on any number.

    "Should I be able to add my own functionality to double or int"

    Yes. Inheritance is one of the fundamental parts That's one of the fundamental parts of an OO system.

    "Should I derive from double or int "

    Yes.

  • Anonymous
    July 07, 2004
    Kavan: Can you explain that a little more. You said: "The idea is not just to throw in all the features that would possibly be useful. If there is any chance of it being misused, why don't you let FxCop do its job of telling what is good use and what is misuse. I really think deciding on usability is not a job for the language but for a reviewer system. "

    You seem to be proposing that we just allow FxCop to make sure that things are being used approriately. (so far I'm with you). But you then say "The idea is not just to throw in all the features that would possibly be useful". So you seem to be saying that we should not be including all features just because they're useful.

    These two statements seem unrelated. One is saying "add features because there is a system to watch for misuse". The other says "don't add features just because they're useful.". When should we add features then?

  • Anonymous
    July 07, 2004
    The comment has been removed

  • Anonymous
    July 07, 2004
    Nich: "Also, if you wanted to stick to that paradigm, you would have to really say that Sin is a property of an angle, which has a number property representing the value. You can't just assign Sin to a number"

    Absolutely correct. however, I was dealing with the fact that currently Sin operates on numbers and not on angles.

  • Anonymous
    July 07, 2004
    Kavan: Another issue:

    Some things just can't be expressed as rules. How would you express rules about (ab)using static import?

  • Anonymous
    July 07, 2004
    Dr. Pizza: I'll respond to each point in turn:

    "So, he says that static imports make code clearer (which is good), but they're bad because someone else might not find it clearer. Which is it? "

    He's saying that there is a tradeoff. That while it might make things clearer for one developer at one point in time, it can end up being less clear for someone who inherits the code. Or for a team working on the code.

  • Anonymous
    July 07, 2004
    Dr. Pizza: "It's not clear to me how static import has any impact on this issue at all. It looks like you want prototype OO or non-final String classes or something along those lines. "

    Yes. That is what I would like. The arguments being made for static import have been along the lines of "because we have classes that don't fit into an OO framework well -> we end up needing static methods to do work -> Using those static methods is a pain -> we'd like static import as that would alleviate that pain."

    My argument is that static import is being introduced as a way to solve a problem with poor design. I would rather address the design issues first and then have the need for this feature fall out.

  • Anonymous
    July 07, 2004
    "He's saying that there is a tradeoff. That while it might make things clearer for one developer at one point in time, it can end up being less clear for someone who inherits the code. Or for a team working on the code. "
    What I don't see is why static imports are more vulnerable to this than other imports.

    In Java, for example, there are a couple of Date classes; java.util.Date and java.sql.Date. If one sees merely "Date" one cannot be sure which is being talked about.

    Should we, therefore, forbid regular imports in favour of full qualifaction too? We see three things called Delegate (two classes and an interface).

  • Anonymous
    July 07, 2004
    Dr. Pizza: "No we shouldn't. "min" is a symmetric operation. It doesn't make sense to have it as a member like that; it says "I'm doing something to x with y as an argument", when in actual fact that's now how minimums work. "

    You're right. I corrected that after I wrote it, but couldn't get the page to save. I left it in because people had already responded to it.

    Minimum is the smalled value of "something". Commonly the smallest value of a set.

    So, what I would prefer is:

    {x, y}.Minimum.

    Or, you could have:

    MyContinuousFunction.Minimum

    Etc.

  • Anonymous
    July 07, 2004
    "Yes. That is what I would like. The arguments being made for static import have been along the lines of "because we have classes that don't fit into an OO framework well -> we end up needing static methods to do work -> Using those static methods is a pain -> we'd like static import as that would alleviate that pain.""
    Those aren't the arguments for static import. The arguments for static imports is that non-members are the right way to implement many functions, and so should be convenient to use.

    "My argument is that static import is being introduced as a way to solve a problem with poor design. I would rather address the design issues first and then have the need for this feature fall out. "
    So you think we should ditch class-based OO and move to prototype-based? Then why don't you write everything in JScript?

  • Anonymous
    July 07, 2004
    Dr. Pizza:

    "Further, you're now forcing me to implement Min on every_single_class I write. The non-member lets me write one implementation of Min<T>(T lhs, T rhs) { return lhs < rhs ? lhs : rhs; } and implement one comparison operator (less-than) which I need to implement anyway and have a function that works for every class that offers less-than. I can't do that with members. I'll need to reimplement it every time. "

    I don't see the difference. In your system you must implement your < operator. This is stating that for Min to work your type must be comparable. That is easy to express in this system as well with < method (or an IComparable interface if your language doesn't support virtual operators).

    In your system you're actually writing:

    Min<T>(T lhs, T rhs) where T : HasTheLessThanOperator

    If T doesn't have the < operator then your min function won't work. Because of this, it is simple to have the concept:

    Set<T> where T : IComparable {
    T Minimum.
    }

    Now "Minimum" is where it belongs (on a set of things);

    Note: in this system you have hte ability to implement minimum as you see fit depending on your set implementation.

    In the system above once you've defined minimum nobody can provide an alternative implementation that follows their own rules. I consider taht a good thing, but I can see how others wouldn't.

  • Anonymous
    July 07, 2004
    Dr. Pizza: "And this thing can't even go into an interface, because the method is a binary method (it needs the object on which the method is being called and the argument to be of the same type). It also needs support for covariant return types. "

    I don't see minimum as being a binary method at all.

    I do see the need for covariant return types. However, lack of that feature shouldn't be part of argument for something else. If we need covariant return types, then we can add them without jumping into static imports.

  • Anonymous
    July 07, 2004
    The comment has been removed

  • Anonymous
    July 07, 2004
    "I don't see the difference. In your system you must implement your < operator. This is stating that for Min to work your type must be comparable. That is easy to express in this system as well with < method (or an IComparable interface if your language doesn't support virtual operators). "
    The difference is that I need less-than for several operations; Min, Max, less-than, greater-than. Less-than is the minimal way of implementing each of these.

    "If T doesn't have the < operator then your min function won't work."
    If T doesn't have the less-than operator then the min function has no semantic meaning and should not work.

    "Now "Minimum" is where it belongs (on a set of things); "
    That may make sense if the set/multiset/bag is what you're interested in. It's less pleasing when you've just got a pair of numbers. In that case you're often not interested in "a set" as such. For example, if you've got a rectangle and you want to find its smaller side, it seems unnatural to me to think "the least entry in the set of side lengths".

  • Anonymous
    July 07, 2004
    "I don't see minimum as being a binary method at all. "
    Two bad for you. Your minimum member is a binary method.

    "I do see the need for covariant return types. However, lack of that feature shouldn't be part of argument for something else. If we need covariant return types, then we can add them without jumping into static imports. "
    But they're solving a different problem.

  • Anonymous
    July 07, 2004
    Dr. Pizza: "a putative String.Contains should offer no advantage over a non-member function using String's existing methods, so should not be a member function. "

    However, what if given string's existing methods Contains is unable to operate in a performant manner because it needs access to part of the state of string that isn't shared out?

    Would you say then that string needs to nothing but expose it's sequence of characters and nothing else? (I'm not saying that that's a bad idea, I'm just curious).

    You certainly would be able to everything on a string with just that char sequence exposed, but would it be preferable?

  • Anonymous
    July 07, 2004
    A few more questions. Given that I can't extend int or double, how and where should I add functionality like Sin, Log, etc? Perhaps some of these will be added in later version of the library, but there will always be functions that are missing.

    Also, deriving from int or double doesn't seem quite right. I don't want a special type of number that has a Sin (or whatever) property. I want to be able to take the Sin of any number, even the non-mathematical version in the standard library. How should I do this?

  • Anonymous
    July 07, 2004
    "I completely disagree. Not all of those contains methods will be identically implemented. I've written contains methods many times and most of them share almost nothing common. For the ones that do there are several options on how to accomplish that without code duplication. One method is through inheritance of that common implementation. "
    It's funny, I've written contains methods exactly no times, because I don't have to (std::search or one of its friends instead).

    I'm also curious as to how they share "almost nothing" in common. I mean, I can understand using different implementations depending on for instance the sizes of the two inputs (if your haystack is large then it's probably worth the overhead of e.g. Boyer Moore), but I can't understand why you'd write something different for string containment than array containment or ArrayList containment.

    "I also disagree that contains is a binary method. IMO Contains is a method on a set. It is a query on that set of whther it contains an element or not. "
    Again, String.contains(String) is a binary method; you can't extract it into an interface because the argument type needs to match the class type.

  • Anonymous
    July 07, 2004
    Dr. Pizza: "That may make sense if the set/multiset/bag is what you're interested in. It's less pleasing when you've just got a pair of numbers. In that case you're often not interested in "a set" as such. For example, if you've got a rectangle and you want to find its smaller side, it seems unnatural to me to think "the least entry in the set of side lengths". "

    Then how do you do Min on a rectangle?

    I would presume you would do:

    Min(rect.Width, rect.Height)

    which to me is as understandable (and as clear) as

    {rect.Width, rect.Height}.Minimum.

    To me, I find the latter more intuitive and consistant.

  • Anonymous
    July 08, 2004
    Dr. Pizza:
    "Two bad for you. Your minimum member is a binary method. "

    Could you explain this. I'm not sure how it's a binary method in the form I presented it.

    Currently it's a property on an interface (the set interface) and i'm not sure how that corresponds to a binary function/method.

  • Anonymous
    July 08, 2004
    Dr. Pizza:
    "Again, String.contains(String) is a binary method; you can't extract it into an interface because the argument type needs to match the class type. "

    Why not? That's certainly something an interface can express:

    interface Sequence<T> {
    bool Contains(T t);
    bool Contains(Sequence<T> s);
    }

    class String : Sequence<Character>
    {
    //etc.
    }

    That interface would allow any sequnce to test for contain of a single element, or of another sequence...

  • Anonymous
    July 08, 2004
    Dr. Pizza: "I also find the notion of a property that performs some (relatively expensive) runtime computation undesirable; at the very least it should be a Sin() method, not a Sin property. "

    I'm not sure why the runtime computation is undesirable with a proerty, but ok as a method...

    Could you explain that a bit more?

  • Anonymous
    July 08, 2004
    I sure don't. Like I say, it depends on the scenario, but when one's dealing with rectangles the whole time one's not really talking about "sets of lengths".

  • Anonymous
    July 08, 2004
    David: "A few more questions. Given that I can't extend int or double, how and where should I add functionality like Sin, Log, etc? Perhaps some of these will be added in later version of the library, but there will always be functions that are missing. "

    Given the limitation of not being able to extend certain types, I would go with the static helper method approach where you add functionality to a preexisting type.

    Then, as time goes on, hope that those functions are refactored into a common location.

    "Also, deriving from int or double doesn't seem quite right. I don't want a special type of number that has a Sin (or whatever) property. I want to be able to take the Sin of any number, even the non-mathematical version in the standard library. How should I do this? "

    This is a problem in any situation. If Sin isn't on "int" and you add it to your own subtype then you have the problem listed above. If it's a static method in a class, then you run into the issue that that min function won't be able to handle some new "numeric" class that you create.

    I'm still struggling over the right thing in the context of sealed types that I can't add appropriate functionality into.

  • Anonymous
    July 08, 2004
    "Could you explain this. I'm not sure how it's a binary method in the form I presented it. "
    'cos you presented it as:
    class A
    {
    A minimum(A rhs);
    }

    But you can't stick "minimum" into e.g. a Number interface, because the argument needs to vary. The type of the argument must match the type it's being called on.

    ================

    "That interface would allow any sequnce to test for contain of a single element, or of another sequence... "
    and consequentially has what's probably the wrong signature.

    ================

    "Could you explain that a bit more? "
    It's a matter of expectation; I think people in general tend properties to be cheap to call (which means minimal computation) but don't have that same expectation of methods. Properties look like field accesses, and so the expectation is that they're free like field accesses.

  • Anonymous
    July 08, 2004
    The comment has been removed

  • Anonymous
    July 08, 2004
    "However, what if given string's existing methods Contains is unable to operate in a performant manner because it needs access to part of the state of string that isn't shared out? "
    I'm not really sure how one would write such a string.

    "Would you say then that string needs to nothing but expose it's sequence of characters and nothing else? (I'm not saying that that's a bad idea, I'm just curious). "
    It would need a few other things like operator+= and so on too, but it shouldn't be a /huge/ amount more than that.

    "You certainly would be able to everything on a string with just that char sequence exposed,"
    Well, no, you wouldn't, which is why I'd go a little above and beyond that.

    "but would it be preferable? "
    As a rule of thumb, when deciding if a method should or shouldn't go in String, yes.

    There are some situations where a member offers a special advantage due to its special knowledge. An example of this is a linked list sort method; a linked list can offer a fairly easy implementation of a merge sort that requires only constant additional storage, which is something a non-member sort function can't really do. But I think in general such circumstances are relatively rare.

  • Anonymous
    July 08, 2004
    The comment has been removed

  • Anonymous
    July 08, 2004
    Dr. Pizza: "cos you presented it as:
    class A
    {
    A minimum(A rhs);
    }"

    I retracted that :-)

    I'll do an edit later to the main page. But .Text was borking on me and I didn't want to change it after people had already asked about it, so I left it as a feedback comment.

    Sorry!

  • Anonymous
    July 08, 2004
    The comment has been removed

  • Anonymous
    July 08, 2004
    Dr. Pizza: "There are some situations where a member offers a special advantage due to its special knowledge. An example of this is a linked list sort method; a linked list can offer a fairly easy implementation of a merge sort that requires only constant additional storage, which is something a non-member sort function can't really do. But I think in general such circumstances are relatively rare. "

    Yup (although I don't know if I agree on the 'rare' part) :-)

    So here's the thing.

    You have your global "Sort" function to take a list and sort it. Because it's written to operate on any list (presumably anything you can index into and move elements around in), doesn't that cause perf problems when a linked list is passed in?

    Conversely, by placing Sort on the list interface, it means that one can just call:

    list.SortYourselfFast!!()

    and know that they'll be getting the appropriate type of sorting for their collection.

    As i mentioned before, this doesn't preclude having a common implementation (which would do the work, but which might be slow on some implementations) and then having classes decide to inherit that functionality or use their own specialized version (like the linked list case above).

  • Anonymous
    July 08, 2004
    "Conversely, by placing Sort on the list interface, it means that one can just call:

    list.SortYourselfFast!!()
    "
    Yeah, but there are things that ought to be a list (much as I hate that name; what I mean is it ought to conform to the C++ notion of a "container") on which sort has no meaning. For example, a STL-style Set or MultiSet class shouldn't have a sort method (because they're sorted containers), but should nonetheless be a "List".

    But then again, the .NET container taxonomy is already pretty rank.

    "You have your global "Sort" function to take a list and sort it. Because it's written to operate on any list (presumably anything you can index into and move elements around in), doesn't that cause perf problems when a linked list is passed in? "
    Then perhaps non-member Sort should only operate on RandomAccessContainers (e.g. deque, arraylist, array). I don't find that a hardship in C++, so I don't think I'd find it a hardship in Java or .NET.

    Sure, you lose some amount of container agnosticism. I'm not convinced that this is necessarily a bad thing.

  • Anonymous
    July 08, 2004
    I like your idea of adding that type of functionality to the intance itself. However, isn't adding methods and/or properties to a given intance of an object increasing the size of the structure (whether it's used or not). As many have pointed out, the number of methods you could add to a simple int could be enormous causing the int structure to become much larger than it is today. I know memory is cheap but it seems simple types should remain somewhat simple. Maybe they should only be added as static methods to the datatype itself.

    If you did go ahead with this method it could be made less confusing by hiding some of the members from intellisense like what is currently done and support the different groups of methods through an interface. I realize that would require me to cast it to get access to it but I don't mind that. Of course I don't mind seeing a huge list of functions either, but that's just me.

  • Anonymous
    July 08, 2004
    I should elaborate on "For example, a STL-style Set or MultiSet class shouldn't have a sort method (because they're sorted containers), but should nonetheless be a "List". "

    Why should it be a IList and not just an ICollection or IEnumerable? Because you can add things to it, which you can't do to an ICollection or an IEnumerable. Unfortunately, IList also implies "sortable" and "indexed access". A Set or MultiSet is more than an ICollection but less than an IList.

  • Anonymous
    July 08, 2004
    Dr. Pizza: "But then again, the .NET container taxonomy is already pretty rank."

    Agreed :-)

  • Anonymous
    July 08, 2004
    Adam: "However, isn't adding methods and/or properties to a given intance of an object increasing the size of the structure (whether it's used or not)."

    Nope :-)

    There is a slight (extremely tiny) overhead with the initial load of the class. But that's it. There is no memory overhead with instances of the type.

  • Anonymous
    July 08, 2004
    The comment has been removed

  • Anonymous
    July 08, 2004
    "Agreed :-) "
    Then do something about it!

    Ditch the collections library as it currently stands. It's not too late. Lots of changes are already being made for generics, so why not do things properly whilst you have the chance.

  • Anonymous
    July 08, 2004
    Dr. Pizza: I'm trying.

    It would be Very helpful if you send this feedback to Krzysztof http://blogs.msdn.com/kcwalina

    The more passionate people there are who want better libraries, then the chances of it happening go up greatly.

    Feedback like "But then again, the .NET container taxonomy is already pretty rank" is something that will certainly get a lot of attention :-)

  • Anonymous
    July 08, 2004
    Dr. Pizza: "Now perhaps you might make all these classes extend, respectively, an Integer abstract base and a FloatingPoint abstract base, which /would/ provide somewhere to stick such methods. But in so doing aren't you forfeiting their ValueTypeness? Not a good tradeoff. "

    Not necessarily. With a little bit of work it's possible, without necessarily losing the benefits that value types currently give you in the CLR.

  • Anonymous
    July 08, 2004
    Dr. Pizza: "Also (I don't know the answer to this as I've never bothered to look), does primitive.method() perform boxing? "

    No, it doesn't.

  • Anonymous
    July 08, 2004
    Dr. Pizza: "Far more useful is the ability to write a function once and use it with different types (i.e. genericity). "

    This is completely possible (and common) in a strict OO system.

    You say "Free functions are the right thing to do for this kind of function"

    However, that seems to be a fundamental disagreement point. I think that they are the wrong thing because I'm not seeing a way for it to be exstencible. Once this static Sin function has been written, I don't see how one adds their new type into it. Similarly with "Contains". Once I write my new object that represents something like a graph, how do make that static Contains function aware of it and able to handle it?

    Note: You mentioned Koenig's lookup before. I think that a variation of that could be used effectively here.

  • Anonymous
    July 08, 2004
    Dr. Pizza: "Then perhaps non-member Sort should only operate on RandomAccessContainers (e.g. deque, arraylist, array). I don't find that a hardship in C++, so I don't think I'd find it a hardship in Java or .NET.

    Sure, you lose some amount of container agnosticism. I'm not convinced that this is necessarily a bad thing. "

    This does worry me :)

    These are the kinds of limitations (along with non-exstencibility) that seem to arise out of trying to push functionality like this to static methods. Because the method has no special knowledge of the type it's working on, it makes certain things impossible to do generically, or it only makes them possible at the cost of something like perf.

    In the example we've been mentioning, if you make it only work on RandomAccessContainer, then you lose the genericity of being able to handle all the types (i.e linked list) that the OO model gives you. If you don't have the RandomAccessContainer restriction, then you lose the ability to have performant implementations of these operations.

    You say it's not a hardship for you. But it is for me. I don't want arbitrary limitations imposed on me when the existing system can do more. This is similar to what I was mentioning before with ref-counting and circular references. You said that it wasn't something that affected you. However, it very much does affect me :-)
    Rather than limiting myself in a way that I see provides at best easier reading in some circumstances, is not the choice I would make for myself.

    Note: It also seems weird to restrict to things that implement RandomAccessContainer. If you're narrowing the scope of an operation to one and only one type, then it seems quite valid to say that this sort method should not be static but rather should be a member on that interface. In essence, that's what you're saying "you can sort anything, as long as it's a RandomAccessContainer". Isn't that something that is much more clearly stated by just placing that operation on RAC itself?

  • Anonymous
    July 08, 2004
    The comment has been removed

  • Anonymous
    July 08, 2004
    Dr. Pizza: I'm curious why you feel "I view it as a dead loss, to be honest."

    You'd be surprised how interested people are in knowing how the community feels about the current .net libraries.

    Another option is to go participate on a project like this (http://www.wintellect.com/powercollections/),
    have it become very popular as people realize how much nicer it is to work with a proper library, and then have the current designers realize that they need to do a better job because people are eschewing the .net libraries for 3rd party solutions.

    However, if you do not tell those who are opening up the discussion on these APIs what your opinion is, then they're just going to thinks "well, i guess people are ok with this". If, on the other hand, they start getting strong negative feedback about how painful things are, or how the designs are currently broken, then people will see that and say "ok... we seriously need to fix this; either before we ship, or soon after".

  • Anonymous
    July 08, 2004
    The comment has been removed

  • Anonymous
    July 08, 2004
    The comment has been removed

  • Anonymous
    July 08, 2004
    "This is completely possible (and common) in a strict OO system."
    What is a "strict OO system"?

    OO does not mean "member functions".

    "However, that seems to be a fundamental disagreement point. I think that they are the wrong thing because I'm not seeing a way for it to be exstencible."
    What do you mean, extensible?

    sinh (for example) is sinh. It's (e^x - e^-x) / 2. It'll be defined that way for floating point numbers, for complex numbers, for anything that can be sinh-ed.

    Write it once (e.g.:
    template<typename T> T sinh(T t) { return (exp(t) - exp(-t)) / 2; }
    )

    and it'll work for any number type you care to throw at it.

    "Once this static Sin function has been written, I don't see how one adds their new type into it."
    But you don't need to. You write a canonical Sin and it'll work for anything. You might want to provide some special overloads (for example, you might want Sin(float) and Sin(double) to use floating point hardware, or something like that), but that's just a bell and whistle. Normal implementations can use N terms of one of its expansions.

    And if I want a new number type that has its own special Sin function? I can just write one and let Koenig lookup find it for me. So I might have a DrPizza.Complex class, and I might want (though there's no real reason /to/ want to do this) its Sin to be:
    Complex Sin(Complex z) { return (exp(i * z) - exp(i * -z)) / (2 * i); }
    instead of the default one. And that'll be fine, because when someone writes:
    using DrPizza.Complex;

    Complex c(0.5, 0.5);
    Complex sc(Sin(c));

    "Similarly with "Contains". Once I write my new object that represents something like a graph, how do make that static Contains function aware of it and able to handle it? "
    You don't, and you don't need to.

  • Anonymous
    July 08, 2004
    The comment has been removed

  • Anonymous
    July 08, 2004
    "I'm curious why you feel "I view it as a dead loss, to be honest." "

    Ultimately because the decision was made with knowledge of the superior alternatives yet those superior alternatives were not taken. As such I can only believe that those superior alternatives were discarded for some fatuous reason.

    "You'd be surprised how interested people are in knowing how the community feels about the current .net libraries. "
    If they are, I would indeed.

    "Another option is to go participate on a project like this (http://www.wintellect.com/powercollections/),
    have it become very popular as people realize how much nicer it is to work with a proper library, and then have the current designers realize that they need to do a better job because people are eschewing the .net libraries for 3rd party solutions. "
    The problem is that a third-party library will always be at a disadvantage; things within the framework will use the framework's classes, and so third-party libraries, though they may be superior for what they do, will always be made to feel like second-class citizens. More often than not, people will then blame the 3rd party libraries for this, and not the framework itself.

    If the .NET container framework's interfaces were not used outside the collections library then a 3rd-party solution might be feasible. Unfortunately, that's not the case; /lots/ of classes implement IList, for example, most of them /not/ containers. Could they just as well implement something STL/JGL-like? Of course. But they don't. And since they don't there'll be an impedence mismatch if one wants to use both styles of library.

  • Anonymous
    July 08, 2004
    ""Done it well" is extremely subjective. One of the goals out there (which I don't agree with) is simplicity."
    Simplicity is good if it doesn't hurt people wanting to do complex things. Simple but no simpler, not just "simple".

    I would also tend to distinguish between "complex" and "complicated"; "complicated" to my mind carries with it a notion of "obfuscated, poorly designed, hard to understand, confusing" and so on; complex just means "not simple".

    A library like the STL requires a small amount of complication to do simple things (specifically, I need two iterators, a begin and end one, to pass through a range, which makes the common case of iterating through an entire collection /very/ slightly more complicated). But it requires no more complication to do complex things; if I want to pass through half the range, for instance, or insert in O(1) an item into the middle of a linked list, or (etc.) it is as simple as possible.

    The .NET library is slightly less complicated for the simple things, but considerably more complicated for the complex things. It has no real way of manipulating partial ranges, for instance (other than slurping everything out into a new container).

    The decision is I think penny wise, pound foolish. It makes a small saving (which, with editors with code templates, is very small indeed) for some things, but incurs big costs for others.

    One repercussion of this is that whilst an efficient implementation of the current framework could be provided atop a good framework, the converse isn't true.

    I don't know much about VB.NET so I will have to look at the "My" thing and see if it offers some kind of resolution.

  • Anonymous
    July 08, 2004
    Dr. Pizza:

    "Similarly with "Contains". Once I write my new object that represents something like a graph, how do make that static Contains function aware of it and able to handle it? "
    You don't, and you don't need to. "

    Could you explain this a little more. I'm finding your brief responses difficult to interpret because of the lack of explanations given to them.

    There is clearly a disconnect. I'm stating a question on how to do something, which implies that (to me), this is something I expect to be able to do.

    If you respond with "you don't need to" you don't really help me understand (or further the discussion) because I don't know why you think that.

  • Anonymous
    July 08, 2004
    Dr. Pizza:

    "The .NET library is slightly less complicated for the simple things, but considerably more complicated for the complex things. It has no real way of manipulating partial ranges, for instance (other than slurping everything out into a new container)."

    Yup

    "The decision is I think penny wise, pound foolish. It makes a small saving (which, with editors with code templates, is very small indeed) for some things, but incurs big costs for others."

    Yup

    "One repercussion of this is that whilst an efficient implementation of the current framework could be provided atop a good framework, the converse isn't true."

    Yup


    What I was saying before was that the designers seem to have felt that going down the route of making simple tasks extremely simple (at the cost of complex tasks) was better than making every task simple (but not As simple as possible).

    I disagree with this philosophy, and I think so do a lot of others. I'm pretty sure that if you voice this you will be able to pull in a lot of community support for an improvement like this to happen.

  • Anonymous
    July 08, 2004
    Face it, Pizza, youre just too sophisticated a developer to be using Microsoft tools - youre represent the sharp pointy end of the pyramid, and Microsoft is targetting the shallow end of the gene pool. I'll meet you at dawn: mixed metaphors at ten paces.

  • Anonymous
    July 08, 2004
    Dr. Pizza: "sinh (for example) is sinh. It's (e^x - e^-x) / 2. It'll be defined that way for floating point numbers, for complex numbers, for anything that can be sinh-ed."

    It will also be defined as :

    Sum_{n=0}^inf frac{x^{2n+1}}{factorial (2n+1) }

    The issue i see with your methodology is that you end up defining your function for every data type that it can handle. On top of that, these functions dont' have the benefit of being actually specialized for the type they are acting on (unless you treat all types as shallow containers that expose all state for the world to manipulate).

    This seems suprising to me because it goes against the pre-existing way to do this (with a message) while gaining in only one catergory (readibility for some).

  • Anonymous
    July 08, 2004
    Dr. Pizza: OO does not mean "member functions". No, it does not. But is does relate to inheritance, encapsulation and polymorphism. The static method based approach seems to go against the first two (the last seems to be ok through Koenig's Lookup).

  • Anonymous
    July 08, 2004
    The comment has been removed

  • Anonymous
    July 08, 2004
    The comment has been removed

  • Anonymous
    July 08, 2004
    Damien: It's Dr. Pizza. He didn't spend 4 years in Pizza Medical school for nothing.

  • Anonymous
    July 08, 2004
    Damien:

    "Pizza would define his sinh thusly

    Sinh<T>(T x) where T:Numeric
    { return (exp(x) - exp(-x))/2; } "

    I completely disagree. :-)

    If you were to write it that way then you might as well have written:

    Sinh(Numeric x)
    { return (exp(x) - exp(-x))/2; }

    The genericity there doesn't seem to get you anything.

  • Anonymous
    July 08, 2004
    It makes no sense to not have a feature like this in some
    capacity.

    I want to use:

    print ("Hello")

    Not:

    Console.WriteLine ("Hello")

    Plenty of the power of languages like Perl and Python comes
    from carefully choosing what functions make sense to be
    exposed at the toplevel, and which ones are better suited
    to be exposed as a class.

    The C# world of `evertying in a class' is OK, but it could be
    better.

    Miguel.

  • Anonymous
    July 08, 2004
    Miguel:
    "I want to use:

    print ("Hello") "

    To do what? Print to your printer? :-D

    I kid. You're absolutely right in that "it could be better" and
    "Plenty of the power of languages like Perl and Python comes from carefully choosing what functions make sense to be exposed at the toplevel, and which ones are better suited to be exposed as a class."

    I'm arguing here from a devil's advocate position. If i can be convinced that it won't be abused and we'll studdenly have things like:

    SpellCheck and ConnectToDatabaseAndMailMerge in the global namespace, then I'll be a lot happier ;-)

  • Anonymous
    July 08, 2004
    Why do you need to be convinced that it wont be abused? Surely, it will.

    Thats ok, though, its not a fatal mistake. If there are any conflicts, it wont compile. Simple.

  • Anonymous
    July 08, 2004
    The comment has been removed

  • Anonymous
    July 08, 2004
    "If i can be convinced that it won't be abused and we'll studdenly have things like"
    Name me a language construct that cannot be abused. Stop designing for Morts.

  • Anonymous
    July 08, 2004
    As an aside, do C# generics let me write a russian peasant's exponentiation function that works equally well on strings as it does on doubles?

  • Anonymous
    July 08, 2004
    The comment has been removed

  • Anonymous
    July 08, 2004
    Dr. Pizza: "In C# 2.0 I can't do the first. I can do the second directly (modulo syntax). I can't do the last, because implementing multimethods is too much like hard work, though perhaps if I made the effort I could get nice operator syntax."

    You can do the last, and it's not that hard :-)
    It's already been done by others and I'm interested in seeing how that could be folded back into the current platform.

  • Anonymous
    July 08, 2004
    Pizza: Stop denigrating Morts. Im a mort(on) and Im getting sick of your anti-mort rants.

  • Anonymous
    July 08, 2004
    Dr. Pizza: "Name me a language construct that cannot be abused. Stop designing for Morts. "

    Well... I mentioned covariant return types.

    Note: this has nothing to do with Morts.

    This has to do with the fact C# does not want to go the route of "include everything and the kitchen sink" just because something can have some benefit.

  • Anonymous
    July 08, 2004
    Cyrus: give me an example of how global methods would be abused and of how it would cost the user? How is this complex?

  • Anonymous
    July 08, 2004
    The comment has been removed

  • Anonymous
    July 08, 2004
    The comment has been removed

  • Anonymous
    July 08, 2004
    "a) Performance suffers because I don't have the advantage of a specialized implementation for my type"
    It's already been stated that iff a member offers a special advantage over a non-member then the function should be a non-member. For Sequence.Contains, that isn't the case. Members offer nothing over non-members using the public interface. Given this, why lock the implementation up in a particular concrete type? Why not make it a non-member so it works for any suitable type?

    "b) My design suffers because I arbitrarily limit my operations to not work on all expected types so that I can fit in this model. "
    Nothing arbitrary about it.

    Having void Sort(RandomAccessContainer) not work on LinkedList is entirely non-arbitrary.

  • Anonymous
    July 08, 2004
    The comment has been removed

  • Anonymous
    July 08, 2004
    "It's already been stated that iff a member offers a special advantage over a non-member then the function should be a non-member"

    Er...

    should be a member.

    Obviously.

    I wish these blog things had proper posting interfaces with editing and so on.

  • Anonymous
    July 08, 2004
    Dr. Pizza:
    ""You can do the last, and it's not that hard :-) "
    No you can't, and yes it is. "

    Yes you can do multi-methods, and it doesn't involve Type.InvokeMember . Activators make this posisble and allow you to treat the new object that does multi-method dispatch exactly the same as you would the original object.

    As to the semantics of multi-methods, that's really not relevant :-)
    You were the one saying "I can't do the last, because implementing multimethods is too much like hard work, and even if I did, I still wouldn't have nice operator syntax. "

    I'm just saying it is possible to do without hard work. If you don't like the semantics of it, then you shouldn't have brought htem up in the first place :-D

    Offtopic. In my personal opinion method resolution would work similarly to how overload resolution works today. If a suitable method couldn't be found (Because of ambiguities) then many options are possible, including picking one based on something like comparing argument by argument and when both match, picking the one that's closest. Or, alternatively, you could jsut throw an exception and say "couldn't figure out the best method to call". Again, this doesn't really concern me. I was just addressing the fact that option 3 definitely was possible.

  • Anonymous
    July 08, 2004
    Dr.Pizza:

    "For Sequence.Contains, that isn't the case. Members offer nothing over non-members using the public interface. Given this, why lock the implementation up in a particular concrete type? Why not make it a non-member so it works for any suitable type?"

    Two things.

    a) Members do indeed offer something. For example, i would implement contains very differently depending on my internal structure.

    b) You can still have be a member and make it work on suitable types. That's one of the nice things about inheritance.

    ---

    "Having void Sort(RandomAccessContainer) not work on LinkedList is entirely non-arbitrary"

    It seems arbitrary to me. Sorting a LinkedList is completely reasonable (and possible to do effciently). However, I can't call this nice Sort method to do it. That seems very arbitrary to me. With a method I can just take any list (be it a RAC or a LinkedList or whatnot) and just call list.Sort() and have it be done.

    With the above restriction I now will do Sort(list) in some situations and list.Sort() in others.

  • Anonymous
    July 08, 2004
    Dr. Pizza: "I wish these blog things had proper posting interfaces with editing and so on. "

    Yup.

    Threading might also be nice. But I've seen so many bad interfaces for that.

    It's astonishing how easy it is to get lost in this entire thread and not know (until you reread everything) if you have addressed all points, etc.

  • Anonymous
    July 08, 2004
    The comment has been removed

  • Anonymous
    July 08, 2004
    "a) Members do indeed offer something. For example, i would implement contains very differently depending on my internal structure. "
    There is a HUGE body of functions for which that's just not the case. Why should they be methods?

    "b) You can still have be a member and make it work on suitable types. That's one of the nice things about inheritance. "
    How do I get that to work with ValueTypes?

    I can't use inheritance with C# struct, so I don't see how this is a correct response.

    "It seems arbitrary to me."
    LinkedList isn't a RandomAccessContainer, so what's arbitrary about it?

    "Sorting a LinkedList is completely reasonable (and possible to do effciently)."
    Well here's the thing. I think that sort should probably operate on iterated ranges, not containers, but that means it can't operate efficiently on linked lists.

    "However, I can't call this nice Sort method to do it. That seems very arbitrary to me. With a method I can just take any list (be it a RAC or a LinkedList or whatnot) and just call list.Sort() and have it be done. "
    It needs to do something different, though. string/vector/deque/array will share the same sort implementation (though not share any common base class save for Object); LinkedList is unique in needing a special implementation. I can't (or at least, shouldn't) treat a linked list in the same way as I can treat an array (though people do, turning O(n) into O(n^2) at the drop of a hat) in other contexts, so why should I here?

    "With the above restriction I now will do Sort(list) in some situations and list.Sort() in others. "
    There should be no Sort(list).

  • Anonymous
    July 08, 2004
    The comment has been removed

  • Anonymous
    July 08, 2004
    "http://www.google.com/search?hl=en&lr=&ie=UTF-8&edition=us&q=activator+.net "
    Remoting? Could you implement multimethods a more roundabout way?

    "It only takes a few hundred (maybe less) lines to create an activator that will do this for you"
    What are those few hundred lines?

    This seems to me to fail the "not that hard" criterion. I think I can get multimethods (at least double dispatch) in "a few hundred lines (maybe less)" of C++, but I certainly wouldn't call it "not that hard".

  • Anonymous
    July 08, 2004
    Dr.Pizza: "Well here's the thing. I think that sort should probably operate on iterated ranges, not containers"

    I don't see the extra benefit of that...

    If i wanted to sort a subsection of a list, i would do just that with:

    list.SubView(4, 15).Sort();

    With your system would your Sort method take 2 iterators and a random access container?

  • Anonymous
    July 08, 2004
    Dr.Pizza: Also, could you tell me more about sorting using two iterators. I'd really like to hear how that's down. Do you have an example of something like QuickSort down that way that I could look at to grok? Thanks!

  • Anonymous
    July 08, 2004
    "I don't see the extra benefit of that... "
    Because list.SubView(4, 15) is unnecessarily expensive with a LinkedList. A putative list.sort(iter1, iter2) isn't. I've already had to iterate through (O(n)) to find the start and end location of where I want to sort. Why would I want to iterate through again just to relocate those spots?

    "With your system would your Sort method take 2 iterators and a random access container? "
    I wouldn't have thought so.

    The RandomAccessContainer has no need for the container itself (it just needs the iterators). There might be a convenience method sort(RandomAccessContainer) that just deferred to the iterator version.

    LinkedList.sort could similarly take a pair of iterators and (slightly differently) a no-args convenience form that sorted the whole container.

  • Anonymous
    July 08, 2004
    The comment has been removed

  • Anonymous
    July 08, 2004
    "Dr.Pizza: Also, could you tell me more about sorting using two iterators. I'd really like to hear how that's down. Do you have an example of something like QuickSort down that way that I could look at to grok? Thanks! "
    Take a look at std::sort.

    The VC++ (Dinkumware) implementation, for instance, is an introspective sort (that is, it's a quicksort that reverts to heapsort if it detects it's going quadratic). It also uses insertion sort (IIRC) for sorting small numbers of elements (better constant factors make it a good trade-off).

  • Anonymous
    July 08, 2004
    Id like to know more about these Activators....

    Dont they require that the type you are proxying for be derivded from MarshalByRefObject?

    Is it possible to make the client a non-MarshalByRefObject while the server is. Do the client and server types have to exactly match?

  • Anonymous
    July 08, 2004
    Dr.Pizza: "Because list.SubView(4, 15) is unnecessarily expensive with a LinkedList. A putative list.sort(iter1, iter2) isn't. I've already had to iterate through (O(n)) to find the start and end location of where I want to sort. Why would I want to iterate through again just to relocate those spots? "

    How is SubView more expensive? In both cases I had to get to the correct start/ends nodes in the linked list (presumably O(n)).

    ---

    ""With your system would your Sort method take 2 iterators and a random access container? "
    I wouldn't have thought so.

    The RandomAccessContainer has no need for the container itself (it just needs the iterators). There might be a convenience method sort(RandomAccessContainer) that just deferred to the iterator version.

    LinkedList.sort could similarly take a pair of iterators and (slightly differently) a no-args convenience form that sorted the whole container. "

    Gotcha.

  • Anonymous
    July 08, 2004
    Damien: I don't know too much on the specifics here. I do know that there were no restrictions on what type you used this on. So I assume (but don't know) that MarshallByBlahBlah is only necessary for actual remoting scenarios. I.e. to get this from one appdomain to another. Something that I don't think is something that isn't necessary for multi-methods, which is why I think it works fine on all types.

    I have no idea if they have to exactly match... it's been a while since I've done anything in this space and all I never attempted something like that.

  • Anonymous
    July 08, 2004
    Now this was some reading.

    What I was trying to say is this: I don't propose that you should just put new features into the language. What I propose is that you should put them in and add proper checks to FxCop to verify it is not being misused.

    I hope this makes it more clear. I really don't get it why you're so obsessed with things being misused. Check the design guidelines. You'll notice that most of the things can be misused. Why don't you take away everything just for the sake that that there won't be a chance of it being misused. That will make for a pretty nice language :)

    I really think you're looking at things from the wrong perspective. Instead of trying to get rid of new features on basis of possible misuse, why don't you welcome new features and think of FxCop rules that will catch those uses that you think are not right. Yes, I know you don't want to write code and rather write blogs :P, but we want new things. It's also true that for some things it's hard to write rules, but at least take this view for the things where this is possible.

    I've also noticed that when you're thinking about maintenance you're often thinking in terms that the one coming after us will be a complete idiot. Not everyone has the makings of a programmer and if it's so hard to check what imports are specified at the top of the file then such a person is not a good programmer. I doubt he would be able to solve any decent problem. You should more often look at things from the view of productivity boosts and not always place maintenance on first place. You need balance in everything.

  • Anonymous
    July 08, 2004
    Dr. Pizza: "The VC++ (Dinkumware) implementation, for instance, is an introspective sort (that is, it's a quicksort that reverts to heapsort if it detects it's going quadratic). It also uses insertion sort (IIRC) for sorting small numbers of elements (better constant factors make it a good trade-off). "

    Thanks! I will.

  • Anonymous
    July 08, 2004
    The comment has been removed

  • Anonymous
    July 08, 2004
    I don't really understand what is misuse of global functions for you. As I see your main point is readability. I say that if a person can't read the imports then don't give them the task of maintaining code. This is a task that is not for everyone.

  • Anonymous
    July 08, 2004
    The comment has been removed

  • Anonymous
    July 08, 2004
    The comment has been removed

  • Anonymous
    July 08, 2004
    Kavan: "Yes, I know you don't want to write code and rather write blogs :P"

    So shoot me ;-)

    It's a slow day and it's fun to talk while doing a long chain of refactorings.

  • Anonymous
    July 08, 2004
    Cyrus: "Trust me, you don't want people here adding things they're not really psyched about."

    Now this is something that I also see as very important. If you don't have someone psyched about the thing then don't do it.

    When I'm creating class libraries I never give the thing out of my hands untill I've done some serious applications using it. Skipping this part is the most usual reason for crapy libraries.

  • Anonymous
    July 08, 2004
    "How is SubView more expensive? In both cases I had to get to the correct start/ends nodes in the linked list (presumably O(n)).
    "
    'cos with offsets you've gotta do it twice.

    See, I'm assuming that you're finding the offsets as a result of a search or similar. That finding is O(n).

    Where I differ from the .NET library is that I'd have the "find" return an iterator. The stupid List<T> returns an index.

    What this means is that I can refer to the location I've just found in constant time (I just dereference my iterator and I'm there).

    The person who designed List<T>, however, has gotta start from the beginning of the list all over again. Sure, it doesn't change the big-O complexity, but it makes the constant factor bigger (he's O(2n) to my O(n); he thrashes his cache again, I don't).

    By using offsets you've got to do all the work it took to find the right location over and over again. By using iterators, you've only got to do it once; the iterators let you save your place.

  • Anonymous
    July 08, 2004
    "I'm not sure how it's roudnabout. You get a method call with parameters, you determine which of the actual methods is the best match for your arguments, you invoke it... It's pretty straightforward... "
    Using an RPC mechanism for implementing multimethods sure seems roundabout to me.

    If it doesn't to you, well... I'd hate to see what you thought was a roundabout way of doing things.

  • Anonymous
    July 08, 2004
    Dr.Pizza:

    "'cos with offsets you've gotta do it twice.

    See, I'm assuming that you're finding the offsets as a result of a search or similar. That finding is O(n).

    Where I differ from the .NET library is that I'd have the "find" return an iterator. The stupid List<T> returns an index.

    What this means is that I can refer to the location I've just found in constant time (I just dereference my iterator and I'm there).

    The person who designed List<T>, however, has gotta start from the beginning of the list all over again. Sure, it doesn't change the big-O complexity, but it makes the constant factor bigger (he's O(2n) to my O(n); he thrashes his cache again, I don't).

    By using offsets you've got to do all the work it took to find the right location over and over again. By using iterators, you've only got to do it once; the iterators let you save your place. "

    Gotcha! That makes sense. Now, you should tell him that :-)

  • Anonymous
    July 08, 2004
    Dr. Pizza: It's not using an RPC mechanism to implement multimethods. It's using a proxy mechoanism to implement multi=methods.

    RPC is implemented using the proxy support that Activator provides.

    How would you implement multi-methods in the current system given that you want be to intereact with instances of a multi-method dispatch type in teh same way they deal with the regular instance of the type?

    A proxy makes sense to me in terms of a good design choice. Because it's a proxy it looks like the original object ot everyone else. And it can handle the act of recieving this message and passing it to teh appropriate one on the actual instance.

    Similarly, that's how RPC works. You get a proxy object back and that proxy object recieves messages sends them over to the wire and invokes them on the actual object.

    I still dont' see why that is roundabout. It's a pretty direct way for implementing multimethods on the current framework with very little conceptual overhead or fuss (both for the producer of the library, and definately for the consumer).

  • Anonymous
    July 08, 2004
    Dr. Pizza: "If it doesn't to you, well... I'd hate to see what you thought was a roundabout way of doing things. "

    I roundabout way would require the consumer to perform black magic in order for this to work. They'd have a type that wouldn't work properly where a normal instance would have worked. They have to invoke method in an indirect manner (like InvokeMember). etc. etc.

    Instead, the consumers now needs to know nothing about this type. They can use the exact same code paths as before and it will work with this type, except now method invocation will select the most appropriate destination at runtime.

    FOr the producer, this wasn't roundabout because all that happened was the original type was wrapped in a new type that simply took the arguments to a method determined what hte appropiate actual method was and passed it to that.

    That seems like a very direct way to accomplish this. Again, I'm very curious how you would prefer to see this!! :-)

    Seriously, I'm always fascinated in learning new ways to solve problems. If there is something more direct, then I'd love to try implementing it and improving my skills here.

  • Anonymous
    July 08, 2004
    The comment has been removed

  • Anonymous
    July 08, 2004
    The comment has been removed

  • Anonymous
    July 08, 2004
    Some thoughts on static imports and other stuff...

  • Anonymous
    July 08, 2004
    All these math examples are predicated on there being some way of specifying what a number is and what operatiors and methods it supports as part of a generic paramater 'where' clause.

    Perhaps thats a good starting point for progress: a way of specifying operators as part of an interface.

  • Anonymous
    July 08, 2004
    The comment has been removed

  • Anonymous
    July 08, 2004
    The comment has been removed

  • Anonymous
    July 08, 2004
    "I roundabout way would require the consumer to perform black magic in order for this to work. They'd have a type that wouldn't work properly where a normal instance would have worked. They have to invoke method in an indirect manner (like InvokeMember). etc. etc. "
    I dunno. To me, using a mechanism that's there for RPC (and that's what Activators are for; Remoting) seems indirect. I don't see how a proxy object can work exactly like the real object anyway, or where the proxy creation is performed.

    Using something like InvokeMember seems a far more direct way of implementing multimethods. It's what InvokeMember is for; it's why it lets you specify your own Binders.

    "That seems like a very direct way to accomplish this. Again, I'm very curious how you would prefer to see this!! :-) "
    I don't see that using a class meant for RPC is particularly direct.

    "Seriously, I'm always fascinated in learning new ways to solve problems. If there is something more direct, then I'd love to try implementing it and improving my skills here. "
    It needs language support. I need to be able to annotate methods to say that they should have dynamic dispatch on more than just this's type.

    I don't think it should be the default; I'm not sure if multimethods should be members or what the annotation syntax should be. I've seen mechanisms looking like:

    void multiMethod(virtual SomeType arg1, virtual SomeType arg2); // use the type of both arg1 and arg2 to perform dispatch

    which seems reasonable. However, it might be nice to emphasise how multimethods work similarly to single dispatch, and perhaps place the parameters on which dispatching is performed in the same position, something like:
    (virtualarg1, virtualarg2, virtualarg3).tripledispatch(nonvirtualarg1, nonvirtualarg2);

    The particular merit of this I think is that the caller can see fairly clearly what's what, though it looks a bit weird.

    And one would probably want to set a policy on how to pick matches; for example, you might demand an exact match (though that potentially creates a combinatorial explosion), or you might say that the left-most arguments should be most accurate (so given (Derived, Base) and (Base, Derived) when invoked with two Deriveds, pick the first), or the right-most arguments (pick the second) or something else entirely.

  • Anonymous
    July 08, 2004
    The comment has been removed

  • Anonymous
    July 08, 2004
    The comment has been removed

  • Anonymous
    July 09, 2004
    I looked up Koenig lookup... Wow, has C++ gotten strange since I stopped programming it 10 years ago.

    I think I understand Koenig lookup, and its seems very powerfull. It is, however, quite difficult to understand what is going on in any given function call.

    If staticly imported methods are accused of being unclear, then Koenig lookup makes things even less clear.

    Frankly, runtime dispatch using multimethods would seem to have more potential for clarity.

  • Anonymous
    July 09, 2004
    The comment has been removed

  • Anonymous
    July 09, 2004
    DrPizza: thanks for the explanation. Seems a lot clearer than the ones I found on the web.

  • Anonymous
    August 18, 2005
    News on every hour. http://www.bignews.com

  • Anonymous
    May 31, 2009
    PingBack from http://outdoorceilingfansite.info/story.php?id=630

  • Anonymous
    May 31, 2009
    PingBack from http://outdoorceilingfansite.info/story.php?id=18266

  • Anonymous
    May 31, 2009
    PingBack from http://woodtvstand.info/story.php?id=6729

  • Anonymous
    June 08, 2009
    PingBack from http://quickdietsite.info/story.php?id=14179

  • Anonymous
    June 09, 2009
    PingBack from http://greenteafatburner.info/story.php?id=2164

  • Anonymous
    June 17, 2009
    PingBack from http://pooltoysite.info/story.php?id=5940

  • Anonymous
    June 18, 2009
    PingBack from http://thestoragebench.info/story.php?id=5371