Live data from Hacker News

I was wrong, reflecting on the .NET design choices

ayende.com

101–110 of 139 posts

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

#101
post #80

Earlier quoted context omitted.

Garbage collection is the bane of smooth framerates. Players notice when the framerate drops. Presuming a pretty typical 60 Frames Per Second you have a tight 16.6ms time budget to do all of the work for the entire frame. All of the physics, all of the sound processing, all of the AI and everything else needs to be sliced up into little bits that can be distributed across the time the game is played. There are many g…

I wonder if the very low-latency GC in Go would be good enough, though? The occasional dropped frame doesn't seem like the end of the world, so long as it remains rare. In practice, most games don't have entirely reliable performance, particularly on low-end hardware.

Depends on the game.

Drop a frame in a competitive First Person Shooter and be ready for death threats.

Drop a frame in an angry birds clone and be ready for 5 stars in a review just because you made your first game.

I suspect you could could get away with quite a bit of GC in most games. But by the time you learn whether or not you could get away with you have fully committed to language for several months. Unless you fully committed to D you are stuck with your memory management strategy. In order to be risk averse game devs dodge GC languages entirely because the benefit is small compared to the potential gain. Combine this with how everyone wants to make the next super great- MMO that will blow everyone away, they think that they must squeeze every drop of perf out of the machine and sometimes they are right.

Lua is hugely popular for scripting in games. World of Warcraft used it to script the UI. Its garbage collector can be invoked in steps. You can tell it to get all the garbage or just to get N units of garbage. If you tell it to get 1 unit of garbage each frame while frugally allocating I expect you could easily meet the demands of many casual games.

Then there are games like Kerbal Space Program. All C# and all crazy inconsistent with performance. It will pause for no apparent reason right as you try to extend your lander legs and cause you to wreck your only engine on a faraway planet. I cannot say with certainty it is GC, but that cannot be helping.

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

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

Java's virtual by default methods has never been an issue for me. On the other hand, some clown making their classes and methods final has been a recurring thorn. David Bacon's Kava's approach to lightweight objects remains the correct answer and is sorely missed. The only case for unchecked exception is mitigating terrible design choices. Like using Spring or Hibernate/JPA. When the client code can't do anything wit…

> C#'s equivalent quixotic fever dream is probably LINQ.

Maybe the weird query-style syntax.

If you use it properly, it's no different than map, fold, filter, etc that are ubiquitous functional higher-order functions., just with T-SQL-ly names.

Linq is so much better than doing all of what it does by hand, with for loops and temporaries galore, the way we once did.

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

#103
post #14

Earlier quoted context omitted.

Whatever the reasons behind it, it is clear that a lot of the effective and high impact industrial languages at various levels of the stack (e.g. c++, ada, Java/C#, python, perl, javascript,etc.) have not managed to incorporate some of the real wisdom learned in earlier systems (e.g. smalltalk an some lisps for OO, etc.) If this was in fact avoidable, it is a sad fact.

This type stupid bloviating is why programming will never be a proper engineering discipline. There are two ways to implement oop: classes aka "object templates" or prototypes. Educate your selves people!

This a valid observation, why have no down voters commented? Oop implementation was studied extensively in 80's, CLOS was very contentious in fact. The lack of training and perspective is stunning.

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

#104
post #99
post #91

Earlier quoted context omitted.

Simple designations like "static typing" and "dynamic typing", even when you bring in the concept of strong vs. weak (Java allows concatenating an int to a string, Python throws an error), aren't very helpful when languages like Common Lisp exist. (Edit: nor "compiled" vs. "interpreted" either for the same reason but especially in current_year when just about everything compiles to some form of bytecode, whether that…

You are right that the type system waters are muddied by a variety of technologies and perhaps that isn't the best line to draw. I think your focus on the semantics of static vs dynamic dodges much of my point. The crux of my argument was the larger and more complex the work the more important it is to find errors early. It seems obvious to me that languages like Java, C++ and Rust do much more to catch errors early…

This is why I think workflows matter too, at least as much as the language itself. If you write Python like you write Java, of course you're going to not catch some errors that Java would have caught before you ship, and you're probably going to be frustrated when you're at a company where everyone writes Python like Java. But if you write Python like Python (you can't write Java like Python), you'll find many of your errors almost immediately after you write it because you're trying out the code in the REPL right away, and writing chunks in a way where that's easier to do in Python.

Maybe a few type errors will still slip by, but you'll have found and fixed so many other kinds of errors much earlier. Kinds of errors that benefit by being caught immediately instead of festering because they passed a type checker. (I've never really found a type error to be a catastrophic-oh-I-wished-we-found-this-sooner type of bug. You fix it and move on. It's not dissimilar to fixing various null pointer exceptions that plague lots of corporate Java code.)

To me your obvious claim is not obvious at all, because the tradeoff space is so much richer than what mere type systems allow. We're not even touching on what you can do with specs and other processes that happen before you code in any language, nor other language tradeoffs like immutability, everything-is-a-value, various language features (recalling Java only recently got streams and lambdas), expressiveness (when your code is basically pseudocode without much ceremony (or even better when you can make a DSL) there's a lot fewer places for bugs to hide)... Typing just doesn't tell that much of a story.

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

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

Java's virtual by default methods has never been an issue for me. On the other hand, some clown making their classes and methods final has been a recurring thorn. David Bacon's Kava's approach to lightweight objects remains the correct answer and is sorely missed. The only case for unchecked exception is mitigating terrible design choices. Like using Spring or Hibernate/JPA. When the client code can't do anything wit…

> C#'s equivalent quixotic fever dream is probably LINQ.

What? LINQ is terrific.

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

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

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 nor…

I have absolutely no experience with Rust, but I know it normally depends on libunwind, isn't that for unwinding the stack?

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

#107
post #63
post #60

Earlier quoted context omitted.

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?

>Do we know any reason for C# not to adopt this as well? Because delegates are a much easier way of dealing with things? Especially with lambda functions. Unless you're talking about the java pattern of passing an entire anonymous class for, let's say, a formatter or something. The reason for C# not to adopt that seems to be that: - It's so terribly awful, who in his right mind is happy to implement yet another anony…

There is some crap like that still kicking around. Why you need to create a Comparator class to provide a single function for sorting or filtering out duplicate items is silly, when a Func lambda would work? Because it's the old way of doing things, back in the 1/2 era, when delegates didn't have all the syntax sugar to make them painless.

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

#108
post #51

Earlier quoted context omitted.

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.

Type erasure is one of the ugliest things in java, on par with the Linq implementation using streams. It's not a pain only if you never write generic code and you are happy with having a lot of duplication. C# implementation is superior by far, and you can easily return newly created objects from a generic method or use an object properly without the need to pass the type of the object as a method parameter in addition of specifying the generic type.

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

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

> + 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…

I find unsigned types more dangerous than signed bounded types. For unsigned types the edge-case is at 0, a frequently used number in eg. arrays. For signed types the edge-cases are at (random) positive and negative numbers.

Having to think more about edge-cases makes the code more dangerous:

    for(uint i = arr.len()-1; i >= 0; i--) {...}

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

#110
post #97
post #56

Earlier quoted context omitted.

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

You can use things like System.GC.TryStartNoGCRegion and GCLatencyMode.SustainedLowLatency to help mitigate the GC pauses though. There is a lot of work that has been going on with the CLR GC code somewhat recently.

What I want is no GC. I might settle for a way to tell it "Do not walk this graph over here, ever, ever." But really, I want to be nowhere near a GC at all, unless it gives me control of pretty much everything it does. I do develop in C#, and I have to work around the GC every step of the way. I really want not to have to work around it, not better work-arounds. For the most part, I simply don't allocate, but that is ugly and non-idiomatic.
Post reply on HN