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 so that 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 (130)

  1. There is one thing here that I disagree with, which is the statement you make about Sin being a property of a number. This isn’t completely correct, IMO.

    Granted, you could ultimately say that any result from a function/transformation is a property of the inputs to that function. However, this results in REALLY bad design. It might be conceptually correct in the OO world, but what is required to understand it grows in leaps and bounds when you represent function results as properties of the collective inputs.

    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, because while they share characteristics, they are two different things (an angle of 2 degrees is not the same as the number 2). You blow type safety out of the water by doing this. Just because a number can be used as input to a function/transformation, doesn’t mean that it HAS that result as a property.

  2. Kavan says:

    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.

  3. Daniel Jin says:

    just curious, doesn’t have anything to do with static import, but why is this allowed?

    using Mojo = System.Int32;

  4. 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)?

  5. Brant: You wouldn’t have to calculate sine unless it was actually asked for. There would be no perf overhead to this πŸ™‚

  6. 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.

  7. 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.

  8. 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?

  9. Dr Pizza 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."

    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?

    "Wow, this is a very good case to consider then."

    No it isn’t. It’s a stupid case to consider. Someone like that isn’t going to be maintaining or developing programs, so their input on the style of the source code is utterly inconsequential.

    "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."

    If they don’t understand what "sin" means in such a context they’re not going to have a snowball’s chance in hell of understanding the code.

    "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,β€œ"

    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.

    "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."

    I don’t think so. Most final/sealed string classes have a good reason for being so.

    "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."

    I’m sorry, but I don’t see that /at all/. A method such as "Contains" should not be a member of String, because it should be generic. It should be applicable to any sequence. I should be able to say Contains(arrayListHaystack, arrayListNeedle) or Contains(stringHaystack, stringNeedle) or Contains(dequeHaystack, dequeNeedle) or…. There should be only one generic/template contains method, and that means it should be a non-member.

    "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.”"

    This is why Koenig lookup is a Good Thing. If I call sin(myCustomNumberObject) then the compiler should look into the DrPizza.CustomNumberObject namespace to find a sin function, iff it cannot find a sin function in currently visible namespaces.

    "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."

    But (class-based, at least) OO doesn’t do this job for us, and languages like Java and C# don’t give us the tools to use OO properly.

    "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)."

    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.

    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.

    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 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.

    And where do we stop? Do we add .sin, .cos, .tan, .arcsin, .arccos, .arctan, .sec, .cot, .cosec, .arcsec, .arccot, .arccosec, .sinh, .cosh, .tanh, .arcsinsh, .arccosh, .arctanh, .sech, .coth, .cosech, .arcsech, .arccoth, .arccosech, .ln, log10, .arbitraryLogarithm(x), .sqrt, .sqare, .cubert, .cube, .arbitraryPower(x), .factorial, .gamma, .polygamma, .superfactorial, .subfactorial, .hyperfactorial, .doublefactorial, .romanfactorial, .risingfactorial, .ultrafactorial, .fallingfactorial, .barnesGfunction, .abs, .floor, .ceiling, .nearestint…?

    "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)."

    No you can’t (because many of these are binary methods). And you wouldn’t want to if you could (because many of these should share their implementation).

    "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."

    At least with containment it makes sense to have a member function (the haystack and the needle are different in nature, unlike the two choices in min). But again, we suffer by making it a member. We must write a new "Contains" method for every enumerable sequence, even though all of those "Contains" methods will be identically implemented. And again, we still can’t stick it in an interface, because it’s still a binary method.

    From this we see that generic free functions allow us to exploit OO better (we can better encapsulate the function of "contains" or "minimum" or whatever) so that we have to write less code. Class interfaces should be minimal; 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.

    This ultimately reduces the amount of learning we have to do; if I can find a subsequence in a String I can find it in a LinkedList or an ArrayList or a Deque or an Array, because they all take the form Contains(haystack, needle).

  10. 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.

  11. Kavan: Another issue:

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

  12. 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.

  13. 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.

  14. Dr Pizza says:

    "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).

  15. 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.

  16. Dr Pizza says:

    "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?

  17. 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.

  18. 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.

  19. Dr. Pizza: "At least with containment it makes sense to have a member function (the haystack and the needle are different in nature, unlike the two choices in min). But again, we suffer by making it a member. We must write a new "Contains" method for every enumerable sequence, even though all of those "Contains" methods will be identically implemented. And again, we still can’t stick it in an interface, because it’s still a binary method. "

    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.

    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.

  20. Dr Pizza says:

    "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".

  21. Dr Pizza says:

    "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.

  22. 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?

  23. 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?

  24. Dr Pizza says:

    "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.

  25. 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.

  26. 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.

  27. 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…

  28. 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?

  29. Dr Pizza says:

    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".

  30. 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.

  31. Dr Pizza says:

    "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.

  32. Dr. Pizza: "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). "

    That’s absolutely true. However, just because things can be unclear currently is not a reason (for me) to introduce more confusing things πŸ™‚

    Note: we have talked to many people that have indeed eschewed imports in order to prevent bugs that have arisen because short qualification has caused bugs.

    Do I understand their point of view? Yes. Do i understand yours? Yes πŸ™‚

    Can both be satisfied. Quite possibly. However, I’m more open to that if you end up making the system simpler in the end.

    Right now all of this is juggling around in my head and I see it as making hte system more complex. That’s a tougher proposition for me to handle because, like Eric, I see simplicity as a feature. However, it’s possible that going down this path might end up making everythin better.

  33. Dr Pizza says:

    "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.

  34. Dr. Pizza:

    "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. "

    Containment on a list will be implemented quite differently than containment on a hash based set, than containment on a tree based structure, than containment on an a graph implemented as an adjacency list, than containment, than containment on an object representing an infinitude of objects vs one representing a finite number.

    Note: this does not preclude a common implementation that does something based on a standard functionality that they all expose. A common pattern that arises is internal/external iteration. It’s certainly reasonable to have something that implements "contains" by jsut iterating over elements looking for a certain object. These other implementations can choose to inherit that implementation, or they can provide their own necessarily specialized versions.

  35. 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!

  36. Dr. Pizza: "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."

    That’s certainly very fair critique.

    Out of curiosity, would you prefer:

    foo.Sin()?

    I tend to think of properties more like data access.

  37. 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).

  38. Dr Pizza says:

    "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.

  39. Adam Miller says:

    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.

  40. Dr Pizza says:

    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.

  41. Dr. Pizza: "But then again, the .NET container taxonomy is already pretty rank."

    Agreed πŸ™‚

  42. 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.

  43. Dr Pizza says:

    "That’s certainly very fair critique.

    Out of curiosity, would you prefer:

    foo.Sin()?"

    No, I still want sin(foo).

    It all comes down to the minimal methods thing. There are, frankly, shitloads of functions that you *could* put into a number type. I listed a few above, but really, that’s just the tip of the iceberg. The thing is, most of those functions aren’t specific to any particular number type (for example, the gamma function will probably be implemented similarly for ints and longs and BigIntegers and so on, and again similarly for floats and doubles and BigDecimals and so on (though differently from the integer implementation)).

    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.

    Also (I don’t know the answer to this as I’ve never bothered to look), does primitive.method() perform boxing?

    Free functions are the right thing to do for this kind of function. Crying "oh, but we’re meant to be OO" carries no weight with me; if your idea of "OO" is making everything a member whether it deserves to be or not, I’m not interested. Far more useful is the ability to write a function once and use it with different types (i.e. genericity). It reduces the maintenance one has to do and affords greater consistency and a steeper learning curve.

    (steep learning curves are good; they mean you learn more in a given time period. It’s shallow learning curves that are to be avoided, as they require lots of effort for minimal reward).

  44. Dr Pizza says:

    "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.

  45. 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 πŸ™‚

  46. 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.

  47. 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.

  48. 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.

  49. 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?

  50. Dr Pizza says:

    "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. "

    How?

    ==========

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

    I view it as a dead loss, to be honest.

    The creators of the .NET collections library ignored the JGL and STL and went for a mediocre Java knock-off (which, again, ignored the JGL and STL for no good reason).

    It’s disappointing in the extreme, but I can’t imagine they’ll ever rectify their decision, because if they were interested in doing it well, they’d have surely done it well in the first place. The path to take was already known, but they didn’t bother with it.

    I’m not claiming that either of those libraries was perfect; they’re not. But they are highly expressive, fairly orthogonal, consistent, extensible systems.

    It seems the big thing that scares people off is the use of half-open ranges defined by paired iterators. Once you’re familiarized with this concept the rest comes naturally.

    And to be honest, I can’t believe it’s all that much more complicated than Java’s brain-dead iterators. They have an interface that not all the implementations bother implementing, and semantics that are downright confusing (the way they point sort of in between elements).

    At least Sun gives a (feeble) explanation as to why they didn’t go for the JGL. MS doesn’t even seem to have bothered explaining why their collections are shit, seeming to be merely content that they /are/ shit.

    I mean, on that linked blog, look at that List<T> crud. All those FindXxx methods. Why the HELL are they there? If I had, say, a LinkedList and I wanted one of those FindXxx methods I would be FORCING my users to do something utterly boneheaded. Why? Because they return a fucking INDEX, which my linked list (not an indexed container) can’t do anything useful with.

    If, on the other hand, I had a proper iterator concept I could just make the method return an iterator and it’d work well for any container. And if I made it a non-member I could use the same implementation for any IEnumerable and could do away with the stupid members entirely.

    But noooo. Iterators (in the STL/JGL style) are a too complicated concept so we’ve got to suffer this pigshit instead.

    It makes me so fucking ANGRY. They had a fantastic chance to do something GOOD but instead they’ve just dropped a load of shit on us and the fucking Morts of this world are too fucking stupid to know any better.

  51. 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".

  52. damien morton says:

    This Mort(on) happens to agree with you, and my voice is hoarse from screaming the same shit in the same direction.

  53. Timbo says:

    As far as Math functions are concerned, I’m all for sticking as close to standard mathematical notation as possible. If I’m writing math on paper, I write sin(x). Regardless of OO ideals, I want to type sin(x) if I’m programming math.

    Similarly, if I had a Function class,

    myFunction.integrate(start, end)

    seems more OO. But that’s not how I write math on paper & it’s not how I envision math in my head. I write (with concessions for this medium):

    { (Sigma) [sub]start[/sub] [super]end[/super] } myFunction

    When programming, I would expect something like Math.integrate(start,end,myFunction). If object-oriented programming says this is wrong, that it should instead be a method on a function, then OO is the wrong ideology for this kind of programming.

  54. Dr Pizza says:

    "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.

  55. Dr. Pizza: First let me say that I agree with a lot of what you say.

    "It’s disappointing in the extreme, but I can’t imagine they’ll ever rectify their decision, because if they were interested in doing it well, they’d have surely done it well in the first place"

    "Done it well" is extremely subjective. One of the goals out there (which I don’t agree with) is simplicity. Take your example on LinkedList. To a mort they dont’ care that Find’ing an index and using that index is slow. They just want it to be simple. Having it return an iterator whose current pointer points to the element you are on would be very confusing for many of them. So, (<em>unfortunately</em>) decisions are made (which again, I don’t agree with) to make things simpler at hte cost of power and extensibility (and even clarity). What’s interesting is that it’s simpler in some ways, and much more complex in others.

    However, we’ve already taken steps to address this. The introduction of the "My" namespace for VB users is a way to insulate people from the complexities of many libraries without making the actual libraries suffer as a result.

    I see it being very possible to provide a rich collections hierarchy, while at the same time having a "My" version which only exposes "List" and "Hashtable". Then you don’t have your desire to make things easy to learn impose on the needs to create extremely capable libraries.

    However, In order for this to get done, the current designers _need_ to know that actual users are frustrated (hell, angry) with what they’ve been given. Please let them know your thoughts on this.

    A good place for this would also be: http://msdn.microsoft.com/ProductFeedback

    I have _no_ doubt whatsoever that if you posted this that you would get _tons_ of votes from other people asking for this to happen.

  56. Dr Pizza says:

    "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.

  57. Dr Pizza says:

    ""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.

  58. 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.

  59. 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.

  60. damien morton says:

    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.

  61. 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).

  62. 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).

  63. Damien: "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. "

    What’s interesting is that when richer APIs are proposed and presented, there is often an incredible amount of backlash from peolple saying "that’s too complicated… I don’t want to learn all that!!".

    Unfortunately, we rarely have people like you two to come in and say "No! Damnit!! We need this!!" THe "My" idea came out of this. A realization that meeting both needs wasn’t really possible. So instead, the idea would be to have a rich library that allowed someone (who was willing to sit down and learn for a bit) to do an incredible amount of stuff, but which would then be filtered down into just a few basic functionalities that would be exposed to a developer who just didn’t care about most of that. I can give examples of this if you’d like.

    I really really believe that the collections are an area where this applies. Collections are an incredibly important tool to any developer. And when they’re good it’s amazing how much more productive you can be. However, for some developers they neither need, nor care about that power, and the robustness and flexibility ends up being a detriment to them. For them, I say give them My.List and My.Hashtable and be done with it. It will serve all of their needs without making the library useless for the rest of us.

    That’s what I"m hoping to get out of thes blogs. Actual feedback and communication to ensure that we dont’ end up screwing the developers who really want to utilize the platform to it’s fullest.

  64. damien morton says:

    Cyrus: there are many languages that manage just fine with static methods in global scope. Python, Dylan, etc etc. People dont go crazy with them, and conflicts are rare. They manage just fine, and they also manage to express some powerfull, generic, concepts with static/generic methods.

    Pizza would define his sinh thusly:

    Sinh<T>(T x) where T:Numeric

    { return (exp(x) – exp(-x))/2; }

    Assuming the BCL derived numeric classes from a base Numeric class/interface, this would work fine.

    Pizza is right. OO means Object-Oriented, not Objects-Only.

  65. Damien: It’s Dr. Pizza. He didn’t spend 4 years in Pizza Medical school for nothing.

  66. 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.

  67. Miguel de Icaza says:

    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.

  68. Miguel:

    "I want to use:

    print ("Hello") "

    To do what? Print to your printer? πŸ˜€

    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 πŸ˜‰

  69. damien morton says:

    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.

  70. DrPizza says:

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

    If you want. It’s a toss up between sticking the Taylor series into e^x or straight into sinh.

    "The issue i see with your methodology is that you end up defining your function for every data type that it can handle."

    Nonsense. I’d define it once, using the definition I already provided, unless I felt a particular type benefited from a special implementation, in which case those types would have a special implementation.

    I don’t have to define it for every type; it’s the same for all numeric types. That’s why it should be in one place. It doesn’t belong to any specific numeric type (it’s a property of all of ’em) so it shouldn’t appear as if it is. Imagining we had a language offering all three of:

    1) unconstrained templates (a la C++; all that matters is that the syntax "works"; that the functions and operators being used are compatible with the instantiation type)

    2) constrained templates (a la .NET; not only must the syntax "work" but the objects must be of a specific type)

    3) subtyping with working binary methods (in truth it’s not quite subtyping; it’s exploiting matching relationships so that binary methods’ arguments can become narrower in derived classes, but it’s close enough; anyway, some kind of multiple dispatch)

    If we had that, I’d be equally happy with:

    T sinh<T>(T t) { return exp(t) – exp(-t) / 2; } // unconstrained templates

    T sinh<T> (T t) where T : Numeric { return exp(t) – exp(-t) / 2; } // constrained generics

    Numeric sinh(Numeric n) { return exp(n) – exp(-n) / 2; } // multiple dispatch

    In C++ I can write the first directly (modulo syntax) and, if I can even enforce the second, if I’m willing to perform clever tricks with templates (specifically, I can mandate that the template parameter extend a Numeric (possibly abstract) base class). 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.

    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.

    Could I do it with plain subtyping and member functions as offered in .NET? Not in any way I’d care to. It means sacrificing operators (which as anyone who’s used Java’s BigInteger library will tell you, is no fun at all), and sacrificing performance (no inlining/static dispatch will be possible). And it’s not clear how a ValueType can exploit subtyping like this anyway.

    "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). "

    No, it assumes they have the basic arithmetic operations needed to perform for example exponentiation and division. Since they do (they are numeric types, after all) this is a sound assumption.

    "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). "

    The statically typed statically bound dynamically dispatched polymorphism that .NET encourages does nto send messages. If you want messages (like Smalltalk or Objective C or IDispatch or whatever) then you’re going to have to write your own runtime on top of .NET, because .NET ain’t it.

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

    "No, it does not. But is does relate to inheritance,"

    Oh? So prototype-based OO is not OO?

    "encapsulation"

    Which I fully advocate, which is why I want minimal public interfaces (containing the essential complexity but no more) and non-member functions. Non-members _force_ encapsulation because they’re not allowed to cheat.

    "and polymorphism. The static method based approach seems to go against the first two (the last seems to be ok through Koenig’s Lookup). "

    At worst it is trading one kind of polymorphism (inlcusion/subtype/inheritance polymorphism) for another (whether it be parametric, as with .NET generics, or overload polymorphism, as with most uses of C++ templates, depends on the exact nature of the language).

  71. DrPizza says:

    "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.

  72. DrPizza says:

    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?

  73. Damien: Because when deciding on what features to add there are a lot of things to be considered. Including:

    a) Cost to implement

    b) Benefit to user

    c) Cost to user (learning, complexity, dealing with abuse)

    etc.

    When deciding on what features to work on, I’d rather do thinks with the most benefit and the least drawback. For example, if I thought a feature would be nothing but abused then it would probably be a pretty bad thing to add when compared to a feature that has high user benefit and very little opportunity for abuse.

    For example, say we have 1 day to implement a new feature. Our choices are this and say covariant return types. Both certainly add complexity and an additional cost to the user to learn and understand. Both benefit the user (to what level is something to be discussed), and one has the potential for abuse (I don’t think I’ve seen an example where covariant returns types abuse anything). So all of these factors would be weighed against eachother to decide which is the better choice given the constraints we have.

    So it is important to me to understand how it will be (ab)used, and how much that weighs in on the decision.

  74. 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.

  75. damien morton says:

    Pizza: Stop denigrating Morts. Im a mort(on) and Im getting sick of your anti-mort rants.

  76. 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.

  77. damien morton says:

    Cyrus: give me an example of how global methods would be abused and of how it would cost the user? How is this complex?

  78. Damien: Eric has mentioned cases. Specifically the issue of a new level of understanding necessary to know what Foo() now means.

    On another vein, Sr.Pizza mentioned the case with LinkedList and sorting, or Contains and any set implementation. Because of this model I seem forced into a situation where either:

    a) Performance suffers because I don’t have the advantage of a specialized implementation for my 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.

    Another, simpler, case was related to what Miguel said. While I was mostly "jesting", the "print" issue is somewhat serious. What does print mean? Will it be overloaded to actually print a document in the future? What if I wanted a method called print? Collisions and clutter are two potential forms of abuse there.

  79. DrPizza says:

    "You can do the last, and it’s not that hard πŸ™‚ "

    No you can’t, and yes it is.

    If you think I’m going to call Type.InvokeMember by hand you’re nuts.

    "It’s already been done by others and I’m interested in seeing how that could be folded back into the current platform. "

    It probably shouldn’t be; the semantics of multimethods are moderately problematic. In particular, there’s no obvious semantics on how to deal with ties (given

    class B {}

    class D : B {}

    void foo(multi B, multi B)

    void foo(multi B, multi D)

    void foo(multi D, multi B)

    what does

    B b1(new D);

    B b2(new D);

    foo(b1, b2);

    do?)

  80. DrPizza says:

    "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.

  81. DrPizza says:

    "Damien: Eric has mentioned cases. Specifically the issue of a new level of understanding necessary to know what Foo() now means.

    "

    It requires no more understanding than "new Foo()" already demands. To figure out what class you’re getting requires you to know what classes are visible. If it’s the same with functions too, what’s the big deal?

  82. DrPizza says:

    "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.

  83. 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 πŸ˜€

    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.

  84. 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.

  85. 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.

  86. DrPizza says:

    (a) what the hell’s an activator? It can’t be that easy, because Google knows nothing about it.

    (b) whatever the hell activators are, they work with operators?

  87. DrPizza says:

    "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).

  88. Dr.Pizza:

    "(a) what the hell’s an activator? It can’t be that easy, because Google knows nothing about it.

    (b) whatever the hell activators are, they work with operators? "

    πŸ™‚

    http://www.google.com/search?hl=en&lr=&ie=UTF-8&edition=us&q=activator+.net

    Yes. Activators work with operators. The basic idea is that given an any time, an activator can return an instance of that type which is actually a proxy to an implementation you control. So in the same way you can have an activator return a client/server proxy that forwards all method to right object, you can create an Activator class that when given any type will return a proxy that looks just like that type (and which works with operators etc.) that you can then implement your multi-methods with.

    It’s pretty fun programming experiment actually πŸ™‚

    Personally, I’ve never needed their power. But here’s a kinda of nifty example of what can be done with them.

    It only takes a few hundred (maybe less) lines to create an activator that will do this for you. Once you’ve written that once, you can then take any type and get a new instance of it what has your multi-method dispatch.

  89. DrPizza says:

    "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".

  90. 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?

  91. 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!

  92. DrPizza says:

    "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.

  93. Dr.Pizza:

    "Remoting? Could you implement multimethods a more roundabout way? "

    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…

    "What are those few hundred lines?"

    I’m not sure if I can post that… I’ll have to find out πŸ™‚

    "This seems to me to fail the "not that hard" criterion"

    If you say so πŸ™‚

    It’s not like it’s hundred of lines per class you want this to work on. And I could very well be wrong. I’ll take a looksee tonight to see how the code was organized and whether or not it was doign more than just multi-methods.

    I think about it this way. Say I wrote this up for you and gave you a dll. You could then use this functionality with no conceptual overhead for yourself (well… except that now you have to understand multimethods). You can use your objects the way you would expect to.

    So "not that hard" refers to the fact that you, the person who wants multimethods, is not sitting there doing stuff like: typeof(Foo).InvokeMember(fooInstance, …, lots of args … etc.) or other weird junk like that which is confusing to read and maintain.

    A one time cost of only a few hundred lines to produce a library which now anyone can use to get this functionality for free seems like a very low price and woudl fall into the "not that hard" category for me.

  94. DrPizza says:

    "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).

  95. damien morton says:

    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?

  96. 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.

  97. 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.

  98. Kavan says:

    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.

  99. 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.

  100. Kavan: "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 totally agree with this. πŸ™‚

    That goes back into that equation I was talking about earlier with tradeoff between benefits and drawbacks. Having an FxCop rule would certainly lower the chance for abuse.

    However, for functions in global space how do you write a rule to prevent abuse (I ask in all seriousness).

    Really what you want is:

    if ( ! Community.BelievesFunctionIsAppropriateInGlobalNamespace(function)) {

    Warn("Danger will robinson!!"); //Note: Warn is a Global Function πŸ˜€

    }

    It’s fine to argue that position, however it’s necessary to show how your solutions will actually prevent the problems that we’re worried about.

  101. Kavan says:

    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.

  102. Kavan: "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 do absolutely take that view. πŸ™‚

    However, as I stated earlier, there are a _lot_ of features we can add. We always need to try to determine which is more important. Factoring in things like abuse is part of that, as is factorign in anti-abuse measures.

    We are always thinking about features and how cool they would be. πŸ™‚

    We just want to implement the coolest ones first.

  103. Kavan: "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. "

    My main point is actually that I feel that these methods actually lead to poorer designs. However, I’m not firm on that position and Dr. Pizza and others are making excellent cases for why this is ok.

    I mentioned this in another post, but i think it’s worthwhile to state again:

    Whenever I see a new feature I immediately do two things:

    a) Get extremely excited about it, bang the hell out of it and find out how useful/great it is

    b) Get extremely distrustful of it and play the devil’s advocate for why it shouldn’t be included.

    If someone (And it happens quite a bit) is able to convince me that a feature is good after I’ve done ‘b’, then I’ll be doubly enthusiastic for getting it into the language. Trust me, you don’t want people here adding things they’re not really psyched about. Chances are they’ll do a half-assed impl and you’ll be really pissed πŸ™‚ *cough*collections*cough*

    THe other good things about ‘b’ is that it ensures that the entire issue is well thought out and that all nigglign issues have been hammered out. THat way we don’t suddenly learn at the end: "Oh crap!! this won’t work because of ‘foo’".

    I’m sorry you only see the ‘b’ side of me here. In other posts and topics the ‘a’ side often surfaces as well πŸ™‚

    I’ll try to let him be more involved in the future!!

  104. 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.

  105. Kavan says:

    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.

  106. DrPizza says:

    "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.

  107. DrPizza says:

    "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.

  108. 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 πŸ™‚

  109. 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).

  110. 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.

  111. Cyrus: "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 was thinking about the second problem for a second, and I realized something. If I’ve implemented Sin or Min, say, as a static function, I’ve done it solely in terms of the public members of a class. If those classes implement appropriate interfaces, I can simply make my static function templated and it will work with any new classes I define. That is, as long as they implement the right interfaces. But if they didn’t, I couldn’t write my static function at all. That is, if Min were written in terms of IComparable, and my new BigNumber class implemented IComparable, my Min would still work. But if BigNumber didn’t implement IComparable (or an equivalent function), it would be impossible to write a static Min function that worked on them. That is, so long as the existing and new classes implement appropriate interfaces, all my static functions should work fine.

    The problem, of course, is that we don’t have an interface for the basic arithematic operations, making it impossible to write a templated Sin function, for example. Operators seem to be an area where C++-style templates come in handy. What do you think of the idea of a "implements operator foo" constraint on generic types?

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

    Me too. Particularly tricky, it seems to me, is the question of where to put the static functions (if that’s how you implement the functionality). Have you ever wrapped a sealed class just to extend it?

  112. Darren Oakey says:

    well…

    I still want static imports πŸ™‚

    Did you see the discussion on Eric’s site about adding methods to an existing class, using some sort of "Extender" notation? To me that would be an even better solution – especially if you could add a method to an interface! (You could even sort of simulate static imports, by implementing the interface <grin> )

    However, my main thing is – I’m a big fan of C#, but only a little fan of OO. A lot of this argument comes down to doing things in the OO way – but I’m quite happy with the way C# is going with 2.0 with iterators, anonymous methods, generics and so forth, and would like it to continue diversifying in the sort of language it is. Steve McConnell in Code Complete II made a really good point about whether you code "in" a language, or into. When I’m coding, I’ll think about part of the system in an OO way, but I’ll think about other parts in an OO way. I don’t think I need multiple languages, because C# is just inches away from being able to just directly code a functional program "into" it…

    As we have all agreed, functional languages don’t see the need for namespaces around functions, functions can just "be"… and I don’t see why it would be a bad thing for me to write mostly functional program in C#.

    (of course that’s just my justification… what it really comes down to, like always – is "most of my code would be cleaner if you gave me static functions, because the line after every function definition would be EnsureNotMissing(, instead of CommonFunctions.EnsureNotMissing(" – which is really my only reason for wanting it. Give me nonnull types, and my request is redrawn πŸ™‚ )

  113. Some thoughts on static imports and other stuff…

  114. damien morton says:

    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.

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

    Now that’s just crazy talk!! πŸ™‚

  116. Dr Pizza says:

    "Now that’s just crazy talk!! πŸ™‚ "

    Yeah, it is, because you would want to define most operators as:

    MostDerivedType operator@(MostDerivedType lhs, MostDerivedType rhs)

    which doesn’t play well with interfaces.

    Actually, you _want_ to define most operators as:

    const MostDerivedType& operator@=(MostDerivedType rhs)

    and have the compiler automatically generate the binary forms as:

    MostDerivedType operator@(MostDerivedType lhs, MostDerivedType rhs)

    {

    return MostDerivedType(lhs) += rhs;

    }

  117. Dr Pizza says:

    "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.

  118. Dr. Pizza: "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"

    That’s certainly quite possible in this scheme (and would probably be encouraged).

    For example you could do this:

    [MultiMethod]

    void SomeMethod(…)

    To apply to the whole method.

    Or, similarly to your syntax:

    void SomeMethod([Virtual]SomeType arg, [Virtual]SomeType arg2);

    Nothing about the current system precludes adding that level of customizability or flexibility.

    I agree with you that one of the important added benefits of this is that it’s very clear what’s going to happen with this type.

    ===

    "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. "

    I’m confused… didn’t you say: "If you think I’m going to call Type.InvokeMember by hand you’re nuts. "

    ===

    "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. "

    Yup. Quite possible with the activator based approach. Just write it using the strategy pattern and allow clients to specify what resolution method they want. To make it simple have a version that doesn’t require the client to specify and default to some accepted common pattern (if there is one).

    ===

    "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. "

    Enh.. Whatever. Indirect to you, direct to me. πŸ™‚

    There are probably a multitude of ways of doing this. Another possibility is fun Reflection.Emit games with interception of virtual methods at runtime. But that’s asking for a lot more work (IMO)

    The reason I consider this direct was that I was already thinking of this in terms of a proxy. An object that looked and behaved just like the object it was proxying, just with the added functionality that it would watch the arguments you passed in and it would choose the right one.

    Given that that was my mental model, Activators seemed natural. Again, I don’t thinl of activators as being for RPC. I see them as being for proxies, and that RPC is just one thing that can be implemented on top of that.

  119. Dr Pizza says:

    "I’m confused… didn’t you say: "If you think I’m going to call Type.InvokeMember by hand you’re nuts. " "

    Absolutely.

    You don’t want to call Type.InvokeMember _yourself_. But its mechanism and extensibility model look like a good way of implementing multimethods. Its very purpose seems to be to allow you to customize dispatch rules. In that sense, it’s _direct_ (using the custom dispatch mechanism to customize dispatch) but (in the absence of built-in support) inelegant for users.

    "The reason I consider this direct was that I was already thinking of this in terms of a proxy. An object that looked and behaved just like the object it was proxying, just with the added functionality that it would watch the arguments you passed in and it would choose the right one. "

    Well, I wasn’t thinking of it in terms of a proxy. I was thinking of it in terms of multiple dispatch. The use of a proxy is just an artefact to get around the lack of built-in multiple dispatch support.

    And I’m still not seeing how you’d be able to use a proxy transparently. How do you go from real object to proxy (and vice versa)?

  120. damien morton says:

    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.

  121. Dr Pizza says:

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

    The principle motivation behind Koenig lookup was, I believe, operator overloading in conjunction with namespaces.

    Consider, for example:

    namespace DrPizza

    {

    struct Foo {};

    Foo operator+(const Foo& lhs, const Foo& rhs)

    {

    return Foo();

    }

    }

    int main()

    {

    DrPizza::Foo f1;

    DrPizza::Foo f2;

    DrPizza::Foo f3;

    f3 = f1 + f2;

    }

    Now, without Koenig lookup, that doesn’t work. the operator+ is inside namespace DrPizza. It needs to be fully qualified:

    f3 = DrPizza::operator+(f1, f2);

    (or somesuch)

    Clearly that’s highly undesirable. No-one wants to have to write that.

    (From memory) what Koenig lookup says is that, in the absence of a visible matching function, the compiler should look within the namespaces of the arguments. Since the arguments are of type Foo, and Foo is within the namespace DrPizza, this means that the compiler will look for a suitable operator+ within the DrPizza namespace. It’ll find this, and allow the code to compile.

    This is a Good Thing, I think. It makes the code more obvious; if I’ve defined a load of functions that use my class within that class’s namespace that more often than not I’m going to want to actually use those functions if I’m using an instance of that class.

    It’s also good OO, for the reason that non-members form part of an objects interface (and good parts at that). They damn well _ought_ to be in scope when operating on an object.

  122. damien morton says:

    DrPizza: thanks for the explanation. Seems a lot clearer than the ones I found on the web.