Question of the Day — What’s Worse?

From Twitter yesterday, I was griping about how once you use a Generics Constraint once and suddenly that same constraint has to immediately ripple through the call stack.  Scott Allen posed me a stumper of a question:

What’s worse: generic constraints, or checked exceptions [in java]?

Oh wow, that’s a nasty choice.  Can I have neither?  I’d call both things Noise Code.  Smelly, useless, noise code.

  • Generics Constraints:  90% of the time it’s just to make the compiler shut up and let my code work.  Dear C# team, could you deemphasize all the COM interop work in C# and making C# do kloogey dynamic typing, and GIVE ME FAR BETTER TYPE INFERENCE ON GENERICS SO I CAN GET SOMETHING DONE (contra/covariance makes your little academic hearts go pitter patter, but that won’t really help me much)!!!!!    Anyway, ReSharper can mostly figure out how to propogate the generic constraints, but it gets confused easily and sometimes the constraints will conflict and…  Human intervention is usually necessary.  Andy Sherwood calls this  “the where tentacles.”  Nice.
  • Checked Exceptions.  Much more pervasive, but IntelliJ can handle adding the “throws blah” stuff without any trouble.  The problem is that checked exceptions are so much more common and pervasive.  Needless to say, I don’t find checked exceptions to be the slightest bit useful.

I guess I’d rather live with the generics constraints than waste time with checked exceptions.  Now, keep checked exceptions out of the language, give me F#/OCaml style type inference instead of a nanny compiler (are you sure it’s okay for this object to do this?  Confirm or deny), and make ALL methods and properties virtual by default, and I’m  a happy camper.  If I want dynamic typing, I’ll use IronRuby.  If I need office integration, I’ll get a contracter to write VB.Net.

 

About Jeremy Miller

Jeremy is the Chief Software Architect at Dovetail Software, the coolest ISV in Austin. Jeremy began his IT career writing "Shadow IT" applications to automate his engineering documentation, then wandered into software development because it looked like more fun. Jeremy is the author of the open source StructureMap tool for Dependency Injection with .Net, StoryTeller for supercharged acceptance testing in .Net, and one of the principal developers behind FubuMVC. Jeremy's thoughts on all things software can be found at The Shade Tree Developer at http://codebetter.com/jeremymiller.
This entry was posted in Uncategorized. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://www.commongenius.com David Nelson

    I absolutely agree with Lucian. Constraining to a particular constructor signature, even if it is the default signature, is a perversion of OOP and the concept of constructors.

    As for virtual-by-default, nothing would more quickly destroy the extensibility of frameworks (not just the BCL), and I am very glad that the .NET designers made the right decision here. If they hadn’t I probably wouldn’t even be using .NET today.

  • http://blogs.msdn.com/lucian Lucian Wischik

    Instead of “constraints on new”, I think a much nicer pattern is to pass a delegate whose job it is to construct the thing. Here’s some code which requires a constructor of signature (int,string):

    Sub f(Of T)(ByVal f As Func(Of Integer, String, T))
    Dim newlyConstructedObject = f(1, “hello”)
    End Sub

    Sub Main()
    f(Function(i, s) String.Format(“{0}:{1}”, i, s))
    End Sub

    This way is more flexible because accessibility of the delegate is as it was at the callsite of the generic function where it’s supposed to be. Also it supports the object-initializer idiom.

  • http://kentb.blogspot.com Kent Boogaart

    Also annoying are the, um, constraints on constraints. Why can’t I constrain to a type with a particular ctor signature? And don’t get me started on operators…

  • http://blogs.msdn.com/lucian Lucian Wischik

    Could you give a complete sample of the sort of thing you want where type inference doesn’t work today? I tried this code, which I thought at first was similar to what you want, and it inferred fine, so I must be missing something.

    public static class Program
    {
    static void Main(string[] args)
    {
    var x = new System.Collections.Generic.List();
    var y = x.Map((i) => i.ToString()).Map((s) => System.Int32.Parse(s));
    }

    static System.Collections.Generic.IEnumerable Map(this System.Collections.Generic.IEnumerable x, System.Func f)
    {
    return new System.Collections.Generic.List();
    }

    }

  • http://codebetter.com/blogs/jeremy.miller Jeremy D. Miller

    Ok, I was terribly wrong about co/contra variance. I want it now, I propose a .Net 3.5 SP2 release just to do that. I’m getting extremely pissed at the number of overloads I’m having write.

  • Stephen

    Cannot say that generic constraints have ever offended me to be honest.. so to me this is a bit confusing.

  • http://codebetter.com/blogs/jeremy.miller Jeremy D. Miller

    @James,

    “there is really no way around generic constraints”

    I don’t agree. I think you could accomplish some of the same goals as generic constraints with more type inference. The compiler can still enforce “implicit” constraints at compile time. For runtime errors, I go for the age old dynamic typing argument: test your code.

  • http://www.honestillusion.com James Curran

    Unless you want your users experiencing bizarre errors are JIT time (or give up the ability to have generic class definitions in separate assemblies), there is really no way around generic constraints.

  • http://elegantcode.com Jan Van Ryswyck

    The cruft that generic constraints bring to the table is something that bothers me almost every day but couldn’t get so nicely expressed in a blog post like yours. Soooo true.

  • Mike Strobel

    @Jeremy: I agree completely that better type inference would be a welcome improvement. Further, that type of improvement is preferable because it mostly affects the compiler and does not significantly alter the language specification.

    Aside from the performance issue regarding methods being virtual by default, there is the issue of versioning. In a perfect world, developers would never forget to seal methods that should not be overridden and would not expose methods as virtual if altering the behavior could break the functionality of the class. Unfortunately, that is a long shot from reality. We can certainly agree to disagree, but I believe that having methods sealed by default results in more maintainable code, particularly when that code is written by junior developers.

  • http://realfiction.net Frank Quednau

    If some public method in a public class of yours has a generic constraint, and in there you instantiate some class with that type argument, how could you avoid by design of a statically typed language that you don’t need the same or a stronger constraint on said class?

    From my experience a design decision may have to be re-thought if the constraints are rippling through the system far too wide. If the constraints just “fall into place” it is rather satisfying. Admittedly, changing constraint decisions is quite painful.

  • http://codebetter.com/blogs/jeremy.miller Jeremy D. Miller

    @Mike Strobel,

    Thanks for the comment.

    In regards to Generics, it’s stuff like:

    public void Do() where T : new() — forget this. I don’t want to have to define that constraint

    THIS CODE:

    ForController(“home”)
    .Map(c => c.Index()).ToView()
    .Map((c, input) => c.Dashboard(input)).ToView()
    .Map(c => c.UserWorkflowData()).ToJson()
    .Map((c, i) => c.MyTaggedItems(i)).ToJson()

    The call to Map(Func func), I want type inference here by default the way it can with a simple arguments.

    “As for making all methods and properties virtual by default, I urge you to consider the performance ramifications.”

    I’m aware of the arguments for the sealed by default argument, and I’ve never bought into the performance argument. There’s far more very large systems built with Java where it’s virtual by default, and they’re doing fine. I think the tax on productivity and code noise is more important than a very minor performance optimization

  • Mike Strobel

    I generally agree with you about typed exceptions, but I’m not sure how much more the C# team can do for generics aside from providing better support for variance. C#’s strict type safety is one of its virtues and helps lead developers into the “pit of success”, as Brad Abrams likes to say. Generic constraints offer a bit more control, though they are more limited in C# than in some other languages (e.g. you cannot constrain to an Enum in C#, but you can in C++/CLI). It seems to me that if you’re looking for more flexibility, you should be actively trying to *avoid* constraints on your generic parameters. That said, I’m curious to hear what changes you think should be made. I’m hardly an authority on the subject, so perhaps you have some insights that I hadn’t considered.

    As for making all methods and properties virtual by default, I urge you to consider the performance ramifications. All method and property accessor invocations would result in a vtable lookup by default. While this may seem like an insignificant performance hit (and for individual method calls, you might be right), but it really adds up. There’s no doubt in my mind that the C# team made the right call here.