Live data from Hacker News

I was wrong, reflecting on the .NET design choices

ayende.com

41–50 of 139 posts

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

#41
post #40

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…

Since we're talking about performance: the time it takes HotSpot to perform this optimization is also a perf hit for your program. At the end of the day, the fastest code is one that doesn't have to run. HotSpot is an impressive technology but the optimizations it has to do to overcome Java's design really only pay for themselves in most frequently executed code paths and only after some time to gather necessary info…

This may be true in general, but the CLR uses bytecode and a JIT compiler, so that point may be a lot less relevant to it. In addition, devirtualization is apparently valuable enough that they're going to add it to the CLR, per the article.

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

#42
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…

FWIW the try!() macro has been replace with the ? operator.

Having spent extensive time with checked and unchecked exceptions I find Rust's error model to be very robust.

There's extensive tooling to handle and transform them, most of the pain comes from developer who are used to sweeping them under the rug(which totally makes sense in prototype land but when I'm shipping something I want guarantees).

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

#43
post #27

Earlier quoted context omitted.

>While you could use the support for them to split human-managed code across separate source files, that would be a horrible practice To continue that type of advice, some say "#region/#endregion" is another language feature that's intended for code generators so that the IDE can collapse specific lines of code and hide it from view. Programmers should not be hand-coding "#region" themselves. That said, there is deba…

Since it's buried at the bottom of the stackexchange: #region/endregion is useful for visually grouping Unit Tests per method under test. They're also occasionally useful to group interface implementations.

You could just use nested classes for that, with the added benefit that the test framework will reflect that organization in its UX.

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

#44
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…

> The kind of exceptions that unwind the stack until some part of the code up the stack catches the exception.

Rust panics result in unwinding the stack:

https://doc.rust-lang.org/nomicon/unwinding.html

try!/? is preferred for handling errors, but if you "know" that a Result or an Option has a value, you can unwrap() or expect() it, and you'll get a panic if you're wrong.

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

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

Thank you. It has just occurred to me I can use this feature at work!

lol

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

#46

Earlier quoted context omitted.

Since it's buried at the bottom of the stackexchange: #region/endregion is useful for visually grouping Unit Tests per method under test. They're also occasionally useful to group interface implementations.

You could just use nested classes for that, with the added benefit that the test framework will reflect that organization in its UX.

The suggestion is appreciated. Thank you.

I have a minor doubt that I won't like the additional level of nesting incurred, but I'll attempt it regardless.

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

#47
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…

While it's true that Swift and Rust don't unwind the stack, this is just an implementation detail.

The comparison to Go is very misleading. Neither Swift nor Rust require you to return a value on error like Go does, nor do they allow you to simply ignore errors. The semantics, not the implementation, is what matters.

Swift's error handling is not "syntax sugar." try/catch in Swift are not macros that desugar into normal Swift. Errors are not returned via the normal return path, but via a dedicated register. Just like stack-unwinding exceptions, Swift errors are part of the core ABI.

https://github.com/apple/swift/blob/master/docs/ABIStability...

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

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

Hairy yes, but really, really powerful.

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

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

As I see it, the Rust (etc) approach avoids two problems:

1. The C problem of forgetting to check error returns. Yes, exceptions (checked or not) also avoid this.

2. The C++/Java/C# problem of exceptions being more expensive than you'd like for common error situations. .NET has sprouted alternatives like `bool tryParse(String, out int)` as a workaround, but on balance I prefer the unified mechanism.

What I don't like is how innocuous `unwrap` looks, or how often it appears in example code.

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

#50

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…

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.

Post reply on HN