Eric Lippert, a former C# compiler team member at Microsoft, wrote about C# language design, semantics, and compiler behavior.
HN commenters recommend his series on covariance and contravariance and his posts on continuation-passing style and async. They also cite him for explaining C# behavior.
I realized after I posted my last question that a language could convert an unexpected exception into some other notice that "something went wrong" (e.g., another exception). But if that behavior is ever triggered, it's not clear what you can do. You have a library reporting that (1) it detected an internal problem AND (2) it did not handle that problem. Any objects you have from that library might be affected, so you can't reliably call functions on them. If the library provides a way to deallocate its objects ( https://blogs.msdn.microsoft.com/oldnewthing/20060915-04/?p=29723 ), you can't trust that it will work. Letting the objects fall out of scope isn't even safe.
By the way, you can get very close to this behavior today with `std::set_unexpected` ( http://www.cplusplus.com/reference/exception/set_unexpected/ ). That page says that the function you set as the unexpected handler must "end either by terminating (calling `terminate` or some other method, such as `exit` or `abort`) or by throwing an exception (even rethrowing the same exception again). If the exception thrown (or rethrown) is not in the function's dynamic-exception-specification but `bad_exception` is, a `bad_exception` is thrown" (emphasis added).
*
> Programs can already crash in enough ways, without C++ introducing more. ... When I see terminate() I know I have a problem. I essentially have to start using "grep" for every possible "throw" statement (hopefully finding something in…
Microsoft runs internally similar tools. The usual deal is that grandfathered issues (ie. issues found by new code analysis tools on old code base) are generally not fixed, and the warning is instead silenced (all tools have means to suppress specific warning for specific code place). This is done because the risk to fix a code analysis issue on old code base is often higher than the benefit.
New code on the other hand is usually required to pass all static analysis without introducing new warnings, and is often enforced by automated code review (bots that runs static analysis will mark the code review and it will not be possible to check it in).
From time to time some teams form to work on fixing static analysis code warnings on old code, but is not a high priority. Of course a public PR event like the OP may trigger some action, is going to be up to the individual team priorities, policy, schedule, workload etc.
This may sound like laziness or carelessness, but these are good engineers with plenty of experience that are capable of judging risk vs. benefits. If the code was like this for years and no actual bug was reported, is not always worth modifying it. Eric Lippert has a nice blog about How many Microsoft employees does it take to change a lightbulb? [0] and why some of these 'trivial' changes for a small project add up to a risky change at MS scale.
- [0]…
In the case of generics, C# got them right (but required changes to CLR 2.0) and then Java tried to copy the idea (without requiring any changes to the JVM) but fucked them up. Look up "type erasure": Java's underlying compiled classes weren't actually generic, and generics compiled down to "Object" and casts (which are less efficient, and can be subverted at runtime). Generics were implemented by the Java compiler, not the JVM runtime.
https://stackoverflow.com/questions/31693/what-are-the-differences-between-generics-in-c-sharp-and-java-and-templates-i#31866
https://stackoverflow.com/questions/355060/c-sharp-vs-java-generics
http://www.jprl.com/Blog/archive/development/2007/Aug-31.html
http://www.artima.com/intv/generics2.html
https://blogs.msdn.microsoft.com/ericlippert/2009/07/30/whats-the-difference-part-one-generics-are-not-templates/
Related, with emphasis in original:
> Determining whether a given predicate has a set of valuations for its variables which make it true is an NP-complete problem; actually finding the set of valuations is an NP-hard problem. Therefore, overload resolution in C# 3.0 is at the very least NP-hard. There is no known polynomial-time algorithm for any NP-complete or NP-hard problem and it is widely believed that there is none to be found, though that conjecture has yet to be proven. So our dream of fast lambda type analysis in C# 3.0 is almost certainly doomed, at least as long as we have the current rules for overload resolution.
See Lambda Expressions vs. Anonymous Methods, Part Five[0] by Eric Lippert.
[0]: https://blogs.msdn.microsoft.com/ericlippert/2007/03/28/lambda-expressions-vs-anonymous-methods-part-five/
> No one seems to know why C# behaves the way it does.
Operators are lifted[0] over nullability in C#. Every value type T can be converted implicitly to Nullable<T>. When `10 * null` is typechecked, both sides of * are typed as Nullable<T>. The * operator then acts like the pseudo-Haskell
(*) <$> 10 <*> null
or, I guess
liftA2 (*) (Just 10) Nothing
The semantics of Nullable<T> are similar to those of an optional type, with some implicit mapping and lifting. In that context, null acts less like null. (Thanks to convenient conversions.) Note that the following doesn't throw:
int? value = null;
value.HasValue // == false
[0]: https://blogs.msdn.microsoft.com/ericlippert/2007/06/27/what-exactly-does-lifted-mean/
Runtimes and languages go hand-in-hand, a runtime that doesn't support everything that one expects from the standard library breaks compatibility.
Code starts getting full of #ifdefs
Siverlight, .NET Core, WinRT, UWP just to give three reboots.
No support for dynamic APIs, appdomains, IL generation on the fly, reflection APIs done in a different way, ...
Actually C# is not fully backwards compatible, variables declared on foreach statements changed their semantics in C# 5.
https://blogs.msdn.microsoft.com/ericlippert/2009/11/12/closing-over-the-loop-variable-considered-harmful/
MS-DOS is only supported in 32 bit variants, a species in extinsion.