Live data from Hacker News

I was wrong, reflecting on the .NET design choices

ayende.com

51–60 of 139 posts

Re: I was wrong, reflecting on the .NET design choices

#51
post #4

If I attempt to generalize it, I think most of C#'s differences in language design that have opposites in Java are superior. Examples include: + not virtual by default (so base classes can be changed more easily without breaking/recompiling downstream clients that the base class writer doesn't know about; specifying something as "virtual" should be a deliberate conscious decision by the class author) + value types (f…

Not having type erasure is pretty nice (except that reflection code with generics is really hairy).

I like type erasure and depend on it a lot for Scala and Clojure interop. Any JVM language works with the same objects and methods, and can implement its own type system/syntax on top.

Re: I was wrong, reflecting on the .NET design choices

#52
"Another issue is that my approach to software design has significantly changed. Where I would previously do a lot of inheritance and explicit design patterns, I’m far more motivated toward using composition, instead."

This, more than anything, has dramatically improved the quality of my designs... and made coding fun again.

Immutability and Lambda functions have also had a tremendous impact on my designs.

Is the term Object-Oriented-Programming relevant anymore?

Re: I was wrong, reflecting on the .NET design choices

#53
post #8
post #6

Earlier quoted context omitted.

What do you think of partial classes and methods in term of code quality?

I've always thought of "partial classes" as a language feature motivated by code generators not stomping on programmers' manually entered code. E.g. Winforms generates some declarations in one partial class while the programmer codes UI handlers in the other partial class.

That's where every Visual Studio dev will see them used, because it's been a VS pattern since the beginning. But I create them manually often enough. Overall I use them sparingly, but there are cases where you definitely want a set of things in one class for code-organization, but still want to think of them as different modules for human-organization.

Re: I was wrong, reflecting on the .NET design choices

#54
post #36

Earlier quoted context omitted.

> + no checked exceptions (checked exceptions have benefits in theory but real-world practice shows that it forces programmers to copy-paste mindless boilerplate to satisfy the checked-constraint) As languages like Rust or Swift demonstrate, the issue is less the checked exceptions and more the abject lack of support for them in the language and type system, which ends up making them essentially unusable. > Gosling s…

Neither Swift nor Rust have exceptions, checked or otherwise. The kind of exceptions that unwind the stack until some part of the code up the stack catches the exception. They both handle errors by returning error values, kind of like Go. Rust has a try! macro, which might make you think it's try/catch equivalent, but it's not. It's just a syntactic sugar over error values. Similarly in Swift try/catch/throw is just…

With Swift, that assumes that the function being called reports the error in the first place. Very few functions do.

Cast a large double to an int -- crash. How do you catch that error? You can't. You have to make sure it never happens yourself. The UserDefaults is another great one. All sorts of ways that it can crash your app, none of them can be caught or handled. Your app just crashes. My advice: convert all your object to a text string (like json) and store that. Do NOT store a dictionary representation.

Re: I was wrong, reflecting on the .NET design choices

#55
post #36

Earlier quoted context omitted.

Neither Swift nor Rust have exceptions, checked or otherwise. The kind of exceptions that unwind the stack until some part of the code up the stack catches the exception. They both handle errors by returning error values, kind of like Go. Rust has a try! macro, which might make you think it's try/catch equivalent, but it's not. It's just a syntactic sugar over error values. Similarly in Swift try/catch/throw is just…

> Neither Swift nor Rust have exceptions, checked or otherwise. The point is that both of them have generic error types which "infect" every caller (transitively) in much the way checked exceptions do. That Rust and Swift require explicitly bubbling error values should be a point in favour of checked exceptions.

> That Rust and Swift require explicitly bubbling error values should be a point in favour of checked exceptions

I thought the explicit bubbling was the nicest thing about error handling in Rust. It's usually just a single character ('?') that the editor can easily highlight, and nicely indicates where the operations are that might fail.

I'd add that checked exceptions don't play nicely with general functional/stream operations like "map" which is why Java went with unchecked exceptions for their streams api in Java 8. Rust on the other hand can handle interior failures in such functions nicely, promoting them to a single overall failure easily via collect(), using the blanket FromIterator> implementation for Result.

Re: I was wrong, reflecting on the .NET design choices

#56

Java and C# are seen as "old news" by some here on HN but there's a trove of software engineering wisdom in there. It was a huge boon for Microsoft to be able to learn from Sun's mistakes when they were designing a language that, on paper, is basically the same thing. C#, Java, and Go are all "wonderfully boring" languages which is a divisive topic, but they're all very good at being boring languages. It's not just l…

As a low-level game dev, I consider C# a nearly perfect, wonderful language, tragically self-defeated by garbage collection.

Re: I was wrong, reflecting on the .NET design choices

#57

Earlier quoted context omitted.

> I believe the explosion of factories, abstract factories, and just generally over-engineered object construction and initialization schemes in Java and C# would have been side-stepped if both languages had always had a proper metaclass hierarchy paralleling the regular class hierarchy, as well as some form of local type inference. That's a bit harsh. "Factory" is a term that became prominent in Java as a result of…

First class functions aren't replacements for factories. An abstract factory provides several methods for constructing related objects of different types. The objects come from different type hierarchies, whose inheritance structures mimic each other. For instance, you might have a hierarchy of EncryptionStream and EncryptionKey objects. Both derive in parallel into AESEncryptionStream and AESEncryptionKey. Then you…

Yes, you're absolutely right that functions don't cover all use cases of factories. I was mostly thinking about the "why are there factories everywhere" complaint, which is mostly about factories that just produce one object.

> If we can just hold a tuple of classes, and ask each one to make an instance, then that kind of makes AbstractFactory go away.

That seems like just one particular way to solve things. I guess I don't see what the fuss is about, if we are talking about metaclasses in particular, because we could also solve this problem with generics, and the factory solution doesn't seem that bad to begin with.

> Thus all constructors effectively have the same type signature.

Or turned around, the type system is not expressive enough to assign different types to different constructors, and is incapable of distinguishing them. This matches with my general experience, that metaclasses are useful on the dynamic typing side (Python, Lisp, Smalltalk, JavaScript) but annoying on the static typing side (C++, Haskell, C#).

But of course that makes sense. In a system without static types, the only way to pass a class to a function is through its parameters, so you have to pass the class by value. In systems with static typing, you have the additional option of passing a class through a type parameter, which has the advantage of giving you access to compile-time type checking. Furthermore, there are real theoretical problems with constructing type systems which allow you to use metaclasses involving whether the type checker is sound and whether it will terminate.

Re: I was wrong, reflecting on the .NET design choices

#59

While I'm inclined to suspect that non-virtual by default is better from a design perspective‡, don't assume the point about performance is overwhelming. HotSpot has done devirtualization for a long time. You can detect not only when a method is never overridden, but also when it's never overridden at a particular call site. A virtual method that's never overridden can sometimes have no extra overhead, while a virtua…

Depends. Recently I spent a day measuring every permutation of calculation-related optimizations I could think of for a critical inner code path. Then I noticed I'd lazily used one virtual call for convenience, which had been there since the prototype stage. The three minutes spent removing it was by far the biggest win that day.

Re: I was wrong, reflecting on the .NET design choices

#60
post #50

Earlier quoted context omitted.

I'm not sure C# is even really in the "boring" category; they were way ahead of Java 8 with the functional collection stuff in Linq and they're borrowing lots of concepts from Scala for the latest versions.

In addition to the functional stuff, C# has a lot of syntactic sugar that I wouldn't associate with a "boring" language. If you aggressively use all the syntactic sugar from the latest version of C# and compare that to similarly up-to-date Java code, they'll be worlds apart. The C# will look terse, and to more conservative programmers, rather weird.

Not to mention they still have/invent multiple ways to do the same. The old Tuple, anonymous types and new ValueTuples are pretty much different attempts/iterations to achieve a similar goal. I wonder if/how they will unify them or phase some of them out.

Btw, i kinda like java's Anonymous Classes. Do we know any reason for C# not to adopt this as well?

Post reply on HN