News for hackers. Stuff that matters.
Re:Twenty years of C# with Anders Hejlsberg [audio] by dgritsko on Thursday February 10, 2022 @04:53PM (#30293210)
As a fellow C# aficionado, what do you think it's lacking right now? The main thing I really would like to see is support for discriminated unions - but outside of that, it's pretty awesome.
Re:Twenty years of C# with Anders Hejlsberg [audio] by polskibus on Thursday February 10, 2022 @05:01PM (#30293300)
Nullable references are far from what they could be. When you use them a lot, you often have to override compiler with ! because it is too stupid. I’d also like to have better support for const references as in c++.

I’d like to see more powerful generics, so that something akin to partial template specialisation is possible without going through boxing and unboxing too much.

With regards to safety, well rust has shown what is possible. Async/ await has introduced function colouring which is a kind of bloat.

All in all there are many things that could reduce the bloat, instead of some tiny features that help with value types or to optimise some rare case.

Re:Twenty years of C# with Anders Hejlsberg [audio] by jayd16 on Thursday February 10, 2022 @05:22PM (#30293537)
People call async/await function coloring bloat but I think it's exactly where it needs to be. IMO execution yielding needs to be explicit not implicit for use cases where thread identity matters. How are you going to write a UI framework if thread context can change unexpectedly?
Re:Twenty years of C# with Anders Hejlsberg [audio] by coder543 on Thursday February 10, 2022 @05:42PM (#30293782)
Go doesn't need "coloring bloat", but you can still handle the need to run code on a specific thread using LockOSThread.[0] If you absolutely need your code that interacts with the OS GUI functions to be running on the first thread the OS spawned, the documentation even tells you how to do that.

This is an exceptionally low level detail that very few people should ever have to think about. Is there something else you can point to that's pertinent to more developers?

Having written Go for years (as well as a good bit of Rust), I love not having to worry about async/await being the concurrency model, and thread identity is never something I think about. With the upcoming Go release that brings generics, I think there are some code patterns that inherently benefit from an async/await style pattern where you spawn several tasks and await their results, so I'm looking forward to having a generic pattern to spawn goroutines that return a value into a Promise... but the infectious nature of "real" async/await is not something I would consider beneficial in most languages. Rust is a language that is all about low level details and performance, so it actually makes sense there, but that's a rare exception, in my opinion.

Developers generally shouldn't need to worry about things a good runtime can take care of, just like you don't need to worry about when you're going to free an object in C#... the runtime takes care of these details that are rarely relevant to meeting the developer's objectives. When you absolutely need that control, there are usually escape hatches. If the application you're writing frequently requires unlimited control, you should probably be using an unmanaged language like Rust to begin with.

[0]: https://pkg.go.dev/runtime#LockOSThread

Re:Twenty years of C# with Anders Hejlsberg [audio] by jayd16 on Thursday February 10, 2022 @06:11PM (#30294133)
>This is an exceptionally low level detail that very few people should ever have to think about.

Every popular UI package uses a UI thread pattern. Every UI dev thinks about this.

Re:Twenty years of C# with Anders Hejlsberg [audio] by coder543 on Thursday February 10, 2022 @06:15PM (#30294152)
This should be handled transparently by the library, not the application developer. Which is not to say that UI frameworks developed decades ago got this right.

As I said, Go handles this problem just fine, apparently disproving your original argument, which was the main point. Async/await with explicit yield points isn’t needed to support this pattern.

Re:Twenty years of C# with Anders Hejlsberg [audio] by jayd16 on Thursday February 10, 2022 @06:17PM (#30294167)
As soon as anyone gets it right then perhaps async/await will be the wrong choice but not until then.

My understanding is Go UIs are mostly using html or some other languages/frameworks for UI work. Can you link me to a popular native Golang GUI framework to look at?

Re:Twenty years of C# with Anders Hejlsberg [audio] by coder543 on Thursday February 10, 2022 @06:26PM (#30294294)
There are definitely libraries, such as bindings to GTK: https://github.com/gotk3/gotk3 or Win32: https://github.com/rodrigocfd/windigo

The point remains that it is possible to do these things without async/await, which you seemingly asserted it was not. Everything else is irrelevant, as far as I can tell.

Go absolutely isn’t frequently used to develop native UIs, so of course “popular” is a strange thing to discuss here. Why isn’t it popular? Most likely because the kind of visual UI builder tools used in Visual Studio or Android Studio have never had an equivalent funded for use with Go, due to lack of commercial support for that use case. Beyond that, web UI frameworks are immensely popular these days, and most companies would rather use those, further removing motivation to really “make native GUI happen” in Go, but there are niche use cases out there, as evidenced by the existence of libraries. If Go had come out in the early 2000s, native GUI support would likely have been a much higher priority. This is pretty far off topic, though.

Re:Twenty years of C# with Anders Hejlsberg [audio] by jayd16 on Thursday February 10, 2022 @06:33PM (#30294389)
>The point remains that it is possible to do these things without async/await

My point was simply that async/await is good and useful when you're dealing with main threads. You asserted that that was a niche use case and UI programming should not think about thread, to which I replied that it was exceedingly common.

The frameworks you link to do indeed use the main thread pattern which can be blocked by callbacks into Go. You need to be aware of what code you run on the main thread even in the Go code. The problem is naturally colored. Async/await is useful in dealing with that.

Re:Twenty years of C# with Anders Hejlsberg [audio] by coder543 on Thursday February 10, 2022 @06:50PM (#30294597)
I don’t remember claiming the UI thread couldn’t be blocked by callbacks, but it doesn’t seem onerous to avoid, and it is less onerous than having to recolor all of the code in your application and every library you use. You don’t need an async and non-async version of every library in Go, nor do you need to use hacks like “.Result” that every linter likely screams about.

If you use blocking code in C#, even indirectly, you can absolutely block the UI thread, and you may do so accidentally! One async function calls another which calls another which inadvertently does some blocking computation for a few seconds only under certain conditions, freezing the whole UI. If you instead had each UI handler spawn a Goroutine and hand control back to the UI loop immediately, then you would never have any possibility of one of those “async” handlers blocking the UI loop, because every Goroutine is preemptible, and no other Goroutine is running on the locked OS thread.

That robustness alone instantly makes Goroutines better for this. A framework could easily enforce this pattern such that the developer never has to even think about the extra steps of spawning the handler Goroutine or syncing the results back to the UI thread, if there were any demand for native UI frameworks in Go, which there really hasn’t been. I’ve written somewhat similar frameworks in Go for non-GUI purposes.

You don’t seem likely to change your opinion, so I’ll just leave it there, but… the idea that async/await is suboptimal isn’t new. It’s just hard to implement something else, so it has taken languages a long time to do it. Erlang has obviously existed for a long time. Kotlin similarly decided against async/await, and Java is in the process of moving to lightweight tasks like Go using Project Loom. I’m very surprised C# hasn’t announced any intention to move in that direction.

Re:Twenty years of C# with Anders Hejlsberg [audio] by jayd16 on Thursday February 10, 2022 @07:58PM (#30295341)
>I don’t remember claiming the UI thread couldn’t be blocked by callbacks

You had claimed that worrying about threading "[...] is an exceptionally low level detail that very few people should ever have to think about." When main threads block, UI devs do need to worry about it. Go's Goroutines do not solve it without thought.

> If you instead had each UI handler spawn a Goroutine

If you did that, you would have a mess of a UI. Like I keep trying to get across, that is not how any of these frameworks work. They are all single threaded and order matters. Implicit execution yielding is insufficient.

Kotlin is probably closer to async/await than Goroutines. Functions are marked with suspend, coloring them like async in C#. Task.Result is replaced with .await() in Kotlin. Scopes are explicitly managed and can be bound to threads, much like the fine grained management you get in C#. If C# added launch { } syntax to fire off async methods, I would not mind it at all, although I don't think it would be significantly different. The coloring remains because it is essential to the problem space.

You keep claiming implicit threading is better for UI but you have no examples. That said, you're free to like or dislike async/await as you choose. I am simply trying to inform you of a use case that you are neglecting.

Re:Twenty years of C# with Anders Hejlsberg [audio] by coder543 on Thursday February 10, 2022 @08:31PM (#30295611)
I claimed that thread identity was an unnecessarily low level concern for most developers to need to worry about, not threading itself. Especially if "most developers" are just running async callbacks in a single threaded event loop, then who cares about thread identity? Not most people, that's for sure. JavaScript certainly never makes you think about thread identity, and it is one of the most popular languages for UI development. You could run a Goroutine-style lightweight threading system inside a single thread too... it's not strictly necessary for it to be as advanced and multi-threaded as Go's default implementation.

> You keep claiming implicit threading is better for UI but you have no examples.

"Implicit threading" isn't causing problems here. When you are running multiple async/await tasks at the same time, you have no guarantee of ordering there either, you just get all the downsides of additional syntactic bloat and the possibility of accidentally blocking the executor because the executor isn't preemptive -- it's cooperative. If someone clicks a button and that spawns an async callback task, and then they click a different button that spawns another async callback task, as long as you don't block the executor with bad code, those tasks will run in any order that they please as time is available on the executor and as "await" calls unblock. If you're somehow only allowing (or only considering) the case where only one UI callback task is allowed to run at a time without making the UI feel locked up, then you have exactly as much control from within a Goroutine over the ordering of what happens as you would in async/await... but since you're (presumably, since you should be) in a separate goroutine from the UI thread, you cannot block the UI thread no matter what you do, even though you can if you are running a single-threaded async/await event loop and do anything wrong. A classic C# approach would be to launch a full OS thread for each callback handler, and then only synchronize with the UI thread to update UI state, and for many use cases that is arguably better than async/await. Full OS threads are typically considered too expensive to launch many of them, which is why async/await came about, but for small numbers of concurrent tasks... async/await doesn't offer much advantage over full threads.

To make this even more explicit, I would argue that a very large percentage of UI developers these days are developing SPAs, and most actual work that a SPA does is performed on the backend, not within the frontend javascript. As UI elements are interacted with, the SPA is firing off asynchronous requests through a load balancer, which does not even guarantee that requests will remotely go to the same backing server. Each server is acting as the callback handler from completely different machines, yet those developers do just fine without thinking about the thread identity of random UI handlers. This is extremely multithreaded and unordered.

The differences between classic async/await and goroutines/lightweight tasks are also a lot fewer than you seem to realize, they aren't radically different, but those differences represent significant improvements. You can emulate classic, cooperative async/await on top of preemptive lightweight threads, but you can't go the other way.

Re:Twenty years of C# with Anders Hejlsberg [audio] by jayd16 on Thursday February 10, 2022 @11:21PM (#30296926)
>you have no guarantee of ordering there either,

Here's your fundamental misunderstanding. You absolutely do have control of ordering. Unless you explicitly yield execution, you know you have full control of that thread and will never be pre-empted. That is a useful tool used all over UI dev and game dev.

Re:Twenty years of C# with Anders Hejlsberg [audio] by coder543 on Thursday February 10, 2022 @11:40PM (#30297061)
It's not a misunderstanding on my part, as far as I can tell... if you have any "await" statements in your asynchronous callback, you automatically lose any guarantee of ordering as the runtime yields to other tasks. You do not know whether Task A or Task B will complete first. All you have control over is "critical sections", which will not be interrupted, but also can't contain any I/O (which would yield the executor) or long-running computations (which would lock up the UI) whatsoever. Even the order in which these critical sections are executed across multiple tasks is not guaranteed, so you can't rely on Task A to complete critical section 3 before Task B reaches critical section 2. It isn't ordered!

The only apparent benefit of needing to invoke "await" to break up your implicit critical sections is if you have a crap ton of global state (beyond the UI presentation layer) that you're manipulating without any kind of explicit synchronization at all. That's hardly an inspiring design pattern. You want to talk about messy UIs... that's the kind of mess JavaScript is classically known for, since people could always rely on the single-threaded nature to store everything as a global and manipulate it without a care in the world. Global state should be used sparingly. It's not just an antipattern for maintainability reasons, global state is also generally bad for performance since compiler optimization passes can't be as aggressive around it, and if you ever are running in a multithreaded context, manipulating global state significantly hurts CPU performance as the various cores have to keep synchronizing that global state back and forth.

Perhaps you would like to explain what "control of ordering" means in your context, because not being able to control the order that concurrent tasks complete is a very clear consequence of yielding control to the executor with an "await" statement. If you never yield, then sure, but... that's not very useful in an asynchronous context. You might as well just go back to writing blocking C event loops where every task always runs to completion before any other task can start.

Re:Twenty years of C# with Anders Hejlsberg [audio] by jayd16 on Friday February 11, 2022 @12:13AM (#30297291)
The difference between async/await and pre-emption is that execution will only yield at an await instead of possibly any time. You are guaranteed that two tasks without awaits will not be run concurrently. The magic of async/await is that you can opt out of the yields by avoiding awaits...or yield when you want. You do not need lock style critical sections because every command between yields are essentially a critical section. You can't avoid pre-emption.

And yes, you _can_ guarantee task order A then B by having Task B await A.

However, you're focusing on task order, I'm mostly talking about the order of instructs of a tasks acting like critical sections. Said another way, async/await lets you queue several critical sections in a convenient way. It also gives you the tools to do this on specific scheduling contexts and without thread marshaling or synchronization.

You can bemoan the fact that UI systems are designed around single thread access, that that's messy or whatever but its just the reality. You mention thread synchronization. Exactly so. One of the reasons these frameworks opt for a single UI thread! Every popular UI framework works this way. Dealing with UI threads is inescapable. Running code on a UI thread to access UI state in a serialized way is extremely common and imo async/await handles it well and implicit threading doesn't.

Re:Twenty years of C# with Anders Hejlsberg [audio] by coder543 on Friday February 11, 2022 @12:23AM (#30297363)
> The difference between async/await and pre-emption is that execution will only yield at an await instead of possibly any time.

Yes, that’s a critical section.

Every thread on your computer is constantly being preempted by the OS thread scheduler. It doesn’t matter to your code, just like it doesn’t matter in a preemptive task system, unless you are manipulating global state without a lock and someone else is trying to manipulate it behind your back. Preemption is a feature, not a bug.

> And yes, you _can_ guarantee task order A then B by having Task B await A.

What…? The whole point is that these are separate tasks started by the GUI framework as asynchronous callbacks. They can’t wait on each other because they don’t know about each other. If you think Go code can’t run its own instructions in a specific order… what?! So of course Go could do that too. But that’s not at all what I’m discussing!

> However, you're focusing on task order, I'm talking about the order of instructs of a tasks acting like critical sections.

I just don’t feel like you know how Go works. The order of instructions is the order you write them, barring any funny compiler optimizations which also apply to C#. It’s not “magically” running all instructions of your function in parallel or something. Whether the task gets interrupted or not is irrelevant — it will resume where it left off. As long as you synchronize access to global state, no one will be observe the interruptions to the task, exactly like how your operating system is interrupting your program constantly and you can’t even tell.

“Implicit threading” isn’t even the right term here, since that implies the compiler or runtime is automatically forking your code into parallel sections for efficiency. Go does not do this. If you write a for loop, it executes every loop iteration sequentially. It doesn’t do wacky things like you seem to believe.

> You can bemoan the fact that UI systems are designed around single thread access, that that's messy or whatever but its just the reality.

That’s not at all what I’m “bemoaning”. If that’s what you’ve gotten from this conversation, then I’m utterly bewildered. I’m done trying to get my points across if communication has broken down to this degree.

Re:Twenty years of C# with Anders Hejlsberg [audio] by jayd16 on Friday February 11, 2022 @01:21AM (#30297726)
Parallel and concurrent are not the same. I'm talking about preventing concurrency of UI tasks. You can guarantee A1-A2-B1-B2 instead of A1-AYield-B1-B2-A2 if you have control of what will yield. With pre-emption, A might unexpectedly yield mid-execution and B could run, even when scheduled to a single thread.

>Whether the task gets interrupted or not is irrelevant — it will resume where it left off. As long as you synchronize access to global state [...]

Cooperative multithreading like async/await is leveraging the fact that actually if you control the interruption, THAT can be your synchronization. You don't need locks. UI programming is usually lock free. A UI thread is more commonly used. Understanding that, you can see that pre-emption _is_ sometimes a bug.

Re:Twenty years of C# with Anders Hejlsberg [audio] by coder543 on Friday February 11, 2022 @01:50AM (#30297913)
To quote myself:

>>>> If you never yield, then sure, but... that's not very useful in an asynchronous context. You might as well just go back to writing blocking C event loops where every task always runs to completion before any other task can start.

What you're talking about isn't what most people talk about or experience in regards to C# async, and it's still not an actual benefit. In all cases described so far, you're better off explicitly writing A to call B than to try to misuse an async task executor as a weird queue, especially as you said yourself that there is no yielding involved. It's really that simple. What you have described is incredibly brittle code riddled with implicit dependencies, and since you're not yielding, you are locking up the UI.

Re:Twenty years of C# with Anders Hejlsberg [audio] by jayd16 on Friday February 11, 2022 @04:00AM (#30299010)
You still don't get it. Its not that there is no yielding. Its that you as the developer have precise control of when and where to yield.

...But anyway, forget it. I'll be more than happy to use a multi-threaded UI system when such a thing exists.

Re:Twenty years of C# with Anders Hejlsberg [audio] by hackerfromthefu on Friday February 11, 2022 @12:12AM (#30297276)
One of the really interesting things about C# is the breadth of the language, it handles close to if not every possible kind of program that can be written, and that's by design.

In other words it's a fully capable systems level language as well as an application language.

There are a number of design choices that people use to writing high level applications think are funny but when you reflect on them they are there to allow low level control for systems level or high performance programming.

I used to be baffled by some of the designs until I realised that it's a language for everyone/every system, not just for what I personally am doing right now.

Re:Twenty years of C# with Anders Hejlsberg [audio] by coder543 on Friday February 11, 2022 @12:15AM (#30297302)
I think a more compelling argument would be that C# pioneered a lot of things we take for granted, and it’s impressive how much they’ve been able to change the language over time to keep it relevant as the state of the art advanced, but supporting the legacy of the language requires some tradeoffs.

I agree it is a very capable language, but not every aspect is perfect. I would generally prefer C# over a lot of the alternatives.

Re:Twenty years of C# with Anders Hejlsberg [audio] by int_19h on Friday February 11, 2022 @02:17AM (#30298090)
C# can call into WinRT async APIs as naturally as ones written in C#. How does Go handle this scenario?
Re:Twenty years of C# with Anders Hejlsberg [audio] by coder543 on Friday February 11, 2022 @02:41AM (#30298269)
My discussion was intended to be about two things:

- is function coloring better than alternatives?

- is it possible to interact with APIs that require they be called from a specific UI thread using those alternatives?

The answers are resoundingly “no” and “yes”. The point is that I think C# should at some point adopt lightweight preemptible threads similar to goroutines for the benefits, not that you need to switch to Go for GUI apps on Windows. I don’t agree at all that function coloring for async is a benefit, or that poorly written tasks being able to block the executor is a benefit, which is what the GP basically argued.

C# is a good language, but defending function coloring feels the same as those Go developers who defended not having generics for years. It’s important to be able to recognize the shortcomings in languages so that improvements can be made. Generics will be a benefit to the Go language, even if it took a surprisingly long time to materialize. Async in C# is better than what it had before, but that doesn’t mean it is perfect.

To your question more directly, Go’s runtime automatically uses the underlying native async APIs in most places where it makes sense in the standard library. WinRT probably isn’t used by Go, but I haven’t ever looked. If you’re specifically referring to UI APIs, then this is irrelevant to the discussion, because I’m not saying you should switch to Go for that. Plus, Google doesn’t care about using native windows GUI APIs from Go, so it probably wouldn’t fit your definition of “natural”, since I doubt anyone has put in the effort to clear that bar.

Re:Twenty years of C# with Anders Hejlsberg [audio] by int_19h on Friday February 11, 2022 @06:52PM (#30308459)
Function coloring is what allows WinRT to be a language-agnostic async API (whether we're talking about UI or not is immaterial - it's all async).

I'm well aware of the shortcomings of coloring, but the point remains that it's the only way to do async that does not result in insular ecosystems with a penchant to rewrite everything so as to gain the benefits of their bespoke green threading (i.e. exactly like Go).

This may change if we can even standardize green threads on OS level, such that all languages can target it in an interoperable way. But the experience with the adoption (or rather lack thereof) of fibers in Win32 shows just how difficult this can be.

Re:Twenty years of C# with Anders Hejlsberg [audio] by pjmlp on Friday February 11, 2022 @02:33AM (#30298215)
Kind of, only true when speaking about .NET Native, now with standard .NET + CsWinRT it isn't as natural as it used to be.

More an exercise in patience dealing with the WinRT mess.

Re:Twenty years of C# with Anders Hejlsberg [audio] by tester756 on Thursday February 10, 2022 @05:04PM (#30293335)
>With regards to safety, well rust has shown what is possible. Async/ await has introduced function colouring which is a kind of bloat.

I do wonder how much effort it'd require

Re:Twenty years of C# with Anders Hejlsberg [audio] by binarynate on Thursday February 10, 2022 @04:58PM (#30293267)
I'd love to see support for structural typing. There are some proposals for that:

- https://github.com/dotnet/csharplang/discussions/164

- https://github.com/dotnet/csharplang/discussions/2548

Re:Twenty years of C# with Anders Hejlsberg [audio] by keithnz on Thursday February 10, 2022 @06:21PM (#30294232)
they are working on DUs, but I think it is a bit stalled as it gets a bit odd with their enum syntax, but you can actually do them reasonably simply right now. I have a stack overflow answer here (and a link to a F# example I rewrote using C#) https://stackoverflow.com/questions/63724308/using-c-sharp-9...

The only thing is you don't get exhaustive type checking, now depending on how passionate you are, that can be a must have, but I actually don't mind without.

One of the big lacking things for me is the lack of being able to make types from basic types, sort of like a typedef in C++. F# allows you to do it, but it actually fakes it as .NET itself has no concept of this. This is a PITA if you want types for things like CustomerId which is just an int, but you want it such that CustomerId is typed and can't be passed to other places that would take an int, only if places that take that type. You can model it with a record, but it becomes a bit unnatural and makes mapping it to datastores more effort.

There's a few more things I think they could steal from F#

- a way to do pipe operators - Type providers like https://fsprojects.github.io/FSharp.Data/ - computation expressions (monads without the drama)

Re:Twenty years of C# with Anders Hejlsberg [audio] by throw868788 on Thursday February 10, 2022 @08:46PM (#30295750)
I do think the pipe operator is very tied into the first-class currying support and HM type inference in F#. In C# I think it makes a lot less sense.

To be honest I think each language should play to their strengths and only "steal" things that benefit their current market of users and how they work. If it augments them that's fine. Otherwise you risk cluttering the language with too many ways to do things. If you want all these F# features you probably just want F# - its not too hard to call `dotnet new classlib -lang F#` these days and mix both in together in a solution.

Having worked in a few C++ shops, at least from my experience, it can result in a lot of useless debates and internal wars if you keep on adding different features from different paradigms into the language whilst trying to keep backwards compatibility.

My view is that if you want unions/non-nullability/data vs classes even if C# gives you the features, it feels more natural to code that way in F# due to the environment around the features and years of optimising that code path. It's just less noisy and less workaround'y. i.e. each language typically prioritizes enhancements and features based on what users in that language are having pain points on. In that sense F# is much further on the data/function paradigm than C# is - conversely when doing low level stuff C# is often in front having to cater for that for some time.

Re:Twenty years of C# with Anders Hejlsberg [audio] by piaste on Friday February 11, 2022 @06:12AM (#30300021)
The pipe operator can be replaced by a T.Pipe(Func<T, U>) generic extension function.

In fact, I've defined it and occasionally use it in F# when I'm working with extensive C# fluent APIs, because it looks nicer than breaking them up with a single |>.

Re:Twenty years of C# with Anders Hejlsberg [audio] by throw868788 on Sunday February 13, 2022 @02:22AM (#30319948)
I've defined it myself (when doing C# with functional heavy code but it feels clunky) but there's still edge cases where it isn't as nice especially with functions of multiple args, or functions passed in. Sure C# can do these things with more code, just like F# can jump into some unsafe code, but its nicer in F# IMO. The other thing is that the operator as above is probably allocation heavy especially when needing to complete a function inside the lambda whereas the pipe in F# the compiler just rewrites the code. In hot loops or highly repeated workflows this makes a difference.

C# Fluent API's aren't really piped things since they usually just mutate a variable underneath (hence the ignore often at the end of them in F#). I don't think fluent API's and piped functions are the same thing, and the pipe operator allows any function that matches the signature to be put in.

Re:Twenty years of C# with Anders Hejlsberg [audio] by VikingCoder on Thursday February 10, 2022 @09:44PM (#30296220)
Not C# itself, but I'd love to see XNA come back, or Monogame get support from Microsoft, or something.
Re:Twenty years of C# with Anders Hejlsberg [audio] by pjmlp on Friday February 11, 2022 @02:40AM (#30298266)
Monogame has some support from Microsoft,

https://dotnet.microsoft.com/en-us/apps/games/ecosystem

But the problem is the WinDev silo, they are pretty much against anything not C++/COM.

XNA died because when Phil Spencer left, no one else cared and naturally XNA got rewritten in C++ as DirectXTK.

Re:Twenty years of C# with Anders Hejlsberg [audio] by VincentEvans on Thursday February 10, 2022 @05:19PM (#30293503)
I wish for just one thing: native AOP (aspect oriented programming) aka Python-style decorators and context managers. Everything else I need C# already covers adequately.
Re:Twenty years of C# with Anders Hejlsberg [audio] by giaour on Thursday February 10, 2022 @05:34PM (#30293690)
> aka Python-style decorators and context managers

Genuinely curious: aren't Python decorators basically the same as C# annotations, and aren't context managers covering the same ground as a C# using statement?

Re:Twenty years of C# with Anders Hejlsberg [audio] by VincentEvans on Thursday February 10, 2022 @06:17PM (#30294172)
Based on my experience with C# annotations they require cooperation of the function being annotated or the code that calls it. There has to be code somewhere that checks if an annotation of expected type is present, reads its attributes and possibly executes it, and then proceeds based on results.

Python decorators are basically true proxies. The code being decorated or the code that calls it - does not need to be aware in any way of the decorator’s presence, and the decorator gets inserted into the call stack and has access to all call parameters, including the lambda of the decorated function.

Regarding context managers I would need to come back to this post, as I don’t think IDisposable is capable of solving the same use cases, but it’s not on top of my mind as to why.

Re:Twenty years of C# with Anders Hejlsberg [audio] by giaour on Friday February 11, 2022 @08:40AM (#30301321)
> Regarding context managers I would need to come back to this post, as I don’t think IDisposable is capable of solving the same use cases, but it’s not on top of my mind as to why.

I'm definitely not as fluent in python as I am in c#, but don't context managers just allow you to define an __enter__ and __exit__ methods that are called when a `with` block begins and ends, respectively? That closely aligns to defining a constructor and .close() method on an IDisposable class, then creating an instance with a `using` statement.

Re:Twenty years of C# with Anders Hejlsberg [audio] by hackerfromthefu on Friday February 11, 2022 @12:28AM (#30297397)
According to the interviews Anders has given that has been by explicit design.

Anders very much creates 'pragmatic industry languages' that optimize for the broader use rather than the individual use. An important part of this is ability for various developers to work on each other's code bases, relatively efficiently.

The contrast to this is a Clojure where you can end up creating basically a DSL that's optimized to the problem space, but that language is then unique to the application and with a higher learning curve when a new developer has to pick it up.

In one of his interviews Anders specifically said he doesn't like AOP because it allows the creation of 'magic' that is hard to understand for other developers. And that's very true at the margins - sure the standard use cases of e.g. logging are not so hard to grok, but it's possibly to weave together incredibly complex/hard to understand/hard to reverse engineer/easy to misunderstand systems when AOP is used heavily or exotically.

So it makes perfect sense when considering he is optimising for an 'industry language' not a 'superstar whose code is hard to average developers to work with' language, that AOP is not in the core language, especially considering extensions such as PostSharp are available.

Re:Twenty years of C# with Anders Hejlsberg [audio] by VincentEvans on Friday February 11, 2022 @10:02AM (#30302218)
I’ve heard the same thing mentioned before, but I wonder if the same principles are still held firm or if it’s a new day. I think in the last 20 years C# has accumulated quite a bit of complexity that makes me doubt that the language designers are still firm on this. Take Roslin for an example.

I think that some concerns about possible complexity brought on by AOP are true, but I think they are pretty comparable to what one might be concerned about as it relates to Roslin.

But I am not trying to convince anyone really, I am just answering the posters question: what do I wish was added to C#.

Re:Twenty years of C# with Anders Hejlsberg [audio] by hackerfromthefu on Friday February 11, 2022 @11:20AM (#30303208)
For anyone not familiar, Roslyn is the language server for compilation, analysis and refactoring of code.

I don't understand the premise of your post but I would like to.

How does having these advanced features at the code compilation stage which is used by compiler and visual studio plugin programmers, have any affect on the complexity of application developers? The application developer doesn't have to think about Roslyn/language server at all, they just consume it's features. The application developer is concerned with the complexity/magic in the code, not how the code was compiled?

Re:Twenty years of C# with Anders Hejlsberg [audio] by VincentEvans on Saturday February 12, 2022 @08:25AM (#30312623)
Perhaps I am not referring to the correct technology/feature if you find it so confusing? In my post all I was trying to say is that enabling writing code that generates predicates, expressions etc, literally syntactical building blocks of more code - via code in an inception-like manner - seems to be at least equally complex an endeavor as somewhat arguably non-trivial matter of creating or using python-like AOP decorator “proxies”. So presence of Roslyn kind of disproves that the emphasis on simplicity is still in effect, at least in my mind. Hope that clears up my point.
Re:Twenty years of C# with Anders Hejlsberg [audio] by hackerfromthefu on Friday February 11, 2022 @11:37AM (#30303472)
I've just happened to stumble on this discussion on a C# 11 feature, where someone who I think is on the language team (HaloFour)says ".. using source generators for AOP has been effectively rejected by the language team ..".

https://github.com/dotnet/csharplang/discussions/5735#discus...

Re:Twenty years of C# with Anders Hejlsberg [audio] by VincentEvans on Saturday February 12, 2022 @08:42AM (#30312752)
I don’t think this is a new revelation, but rather someone re-stating what was already stated in the original doc introducing source generators that I’ve read a few month ago, around November 2021-ish? … which said something along the lines that they explicitly making it impossible to use it for AOP.

Personally I think that dogmatic crusade against AOP while adding a menagerie of syntax sugar hieroglyphics :? :: !! :+ … is a bit misguided, but whatever.

Re:Twenty years of C# with Anders Hejlsberg [audio] by VincentEvans on Saturday February 12, 2022 @08:50AM (#30312795)
Re:Twenty years of C# with Anders Hejlsberg [audio] by tester756 on Thursday February 10, 2022 @05:21PM (#30293519)
You mean Java's "Annotation Oriented Programming"?

like @Transactional?

If yes, then I'd really do not want those "magic-like, tricky when it comes to debugging" features

Re:Twenty years of C# with Anders Hejlsberg [audio] by VincentEvans on Thursday February 10, 2022 @05:31PM (#30293650)
Last time I used Java, it looked to me that AOP is supported with compile-time byte-code rewriting, which might explain why it may feel like magic and be hard to debug. .Net also has a IL-rewriting 3rd party toolkits that enable AOP, namely Postsharp - but my experience with it wasn’t great for similar reasons you stated. Maybe I did something incorrectly, but my impression of tools that generate code at compile time - is that you can’t step through the resulting code. Python on the other hand is both flexible and simple. The decorator code simply wraps the function it decorates (you get a lambda variable that will contain the function you annotate, so you do whatever it is you need to do, and then call the original function. Or not, your choice. ) and doesn’t feel complicated at all. Debugging is simple.

Try it and you might change your mind - perhaps C# designers could use a similar source of inspiration.

Re:Twenty years of C# with Anders Hejlsberg [audio] by yread on Thursday February 10, 2022 @06:24PM (#30294275)
There is PostSharp for that https://www.postsharp.net/
Re:Twenty years of C# with Anders Hejlsberg [audio] by VincentEvans on Thursday February 10, 2022 @06:47PM (#30294568)
I mention postsharp in my other reply, but I don’t see it as a compelling substitute - because it’s not a native feature of the language or framework, but also because its compile-time byte-code rewriting nature makes it hard to work with and debug. It fills a niche for sure, but I’d rather it be filled natively by the language itself.
Re:Twenty years of C# with Anders Hejlsberg [audio] by Izikiel43 on Friday February 11, 2022 @04:22AM (#30299183)
Source generators might help with this.
Re:Twenty years of C# with Anders Hejlsberg [audio] by VincentEvans on Saturday February 12, 2022 @08:49AM (#30312792)
Unfortunately the original doc introducing the feature makes an explicit point to say that the feature does not enable AOP.

First question in the FAQ section:

https://devblogs.microsoft.com/dotnet/introducing-c-source-g...

Re:Twenty years of C# with Anders Hejlsberg [audio] by Izikiel43 on Friday February 18, 2022 @03:13AM (#30383299)
That’s a shame.
Re:Twenty years of C# with Anders Hejlsberg [audio] by jayd16 on Thursday February 10, 2022 @05:56PM (#30293940)
I think Kotlin's type delegation is probably a better way to handle method overrides/wrapping. Fits into the language better.
Re:Twenty years of C# with Anders Hejlsberg [audio] by tonyedgecombe on Friday February 11, 2022 @02:56AM (#30298387)
More about .Net than C# but a consistent story on UI code, something I could expect to rely on for the next ten or twenty years.
Re:Twenty years of C# with Anders Hejlsberg [audio] by soupshield on Thursday February 10, 2022 @07:00PM (#30294730)
We have extension methods; I would like extension properties.
Re:Twenty years of C# with Anders Hejlsberg [audio] by hackerfromthefu on Friday February 11, 2022 @12:32AM (#30297432)
I agree this could have compelling uses.