Yes, this is a great idea, and people would do it if there was widespread support for a binary structured format.
The difference between this incredibly ignorant sarcasm and my point is that IDEs already support the thing that I'm suggesting. You can already hover over a method and it'll give you information about the types of the arguments and return values that are specific to the call site.
> At the end it's a balance and subjective preferences
...and you completely missed the main point of my comment, which was not about what information to show, but avoiding duplication due to function coloring:
> And, it absolutely should not require creating two identical function definitions to get around the function coloring problem
I'm afraid I don't follow. It's impossible for you to have two identical functions (one sync and one async), since then they would both be sync. What did you mean?
Alternately, I want to be able to designate a computationally-intense function call (just that call, leaving the other calls alone) as async so that control yields to the event loop.
The main problem is that someone got it in their head that the function definition was the right place to designate whether a function was async or not, and it's not. The right place is the call site.
This unnecessarily trivializes the technical problems at hand. This wasn't just something someone got in their head, especially considering...
> that the function definition was the right place to designate whether a function was async or not, and it's not. The right place is the call site.
This is exactly what JavaScript did. The program only yields at `await`ed callsites and doesn't yield at all other callsites.
`async` only tells the VM to create the state machine necessary to resume the function after `awaited` calls return.
Making a language that automatically generates two versions of a function, one that is async and returns a Promise-wrapped object, and another that is sync, is not hard.
> This is exactly what JavaScript did.
No, it isn't. JavaScript requires you to declare functions as async at the function definition, and you can't call async functions from sync ones. This is exactly the thing I am arguing against, and is the opposite of "The right place is the call site."
> No, it isn't.
Yes, actually, it is. Async functions are a concept that only affects the internal structure of a function - to generate the state machine that allows yielding at `await` keywords and resuming after they resolve. Externally they are no different from "sync" Promise-returning functions.
So they only thing that async functions do is enable yielding at the callsite, the `await` keyword is what actually yields at the callsite and you can await anything, not just async functions.
Again:
> I want to be able to designate a computationally-intense function call (just that call, leaving the other calls alone) as async so that control yields to the event loop.
This is what `await` is - `await` yields. Non-await calls don't yield.
This comment shows a true departure from reality. Computing is filled to the brim with ideas that are easy but are not implemented for reasons other than difficulty.
> I suppose we should all await your backwards compatible proposal?
Your sarcasm merely helps to further demonstrate that you're not interested in a serious discussion.
> Yes, actually, it is.
Then it should be trivial for you to show me an instance of a sync function calling an async one, synchronously waiting for it, then getting back the return value, without clever hacks.
.NET doesn't really need to provide two separate utility methods like this though, because you can use Task.wait to block until the async task is done.
Their async variants call different OS APIs for asynchronous operation where available (Overlapped IO on Windows, on Linux it is specially scheduled (p)read and write calls).
A similar distinction applies to Socket where asynchronous calls use an internal epoll-based engine while synchronous ones are plain send/recv.
Generally speaking, in synchronous code there is no advantage to calling `.Wait()` or `.GetAwaiter().GetResult()` over synchronous APIs when such are offered, and if you can help it, it is best to update the code using them to be async too if your application is multi-threaded. Luckily it's quite easy in most situations unlike what HN public (that hates better languages like C#) would lead you to believe. But ff you do have to do block on waiting for a task, the impact on throughput is usually negligible if you do so within say threadpool worker - the implementation nowadays can cope with this very well and is more resilient to scenarios that used to be problematic.
Of course, in languages like Rust with multiple colors of async code (single-threaded or multithreading-capable), this would get very messy.
[0] As a major caveat, synchronous code also enforces various state transitions much more efficiently. Want to prove that IO is done (in the sense that the user code is done with it) before closing a file? This is pretty easy in single threaded blocking code.
Oh yes, absolutely. But the solution to that is stop using joke languages that can't do kind polymorphism.
> Yes, and the IDE can show you that information, exactly like it does the types of arguments.
How can it, if the language doesn't give it that information? Either you have a language that can reliably distinguish between sync and async calls in a standard way - which is to say, it has a type system that distinguishes between sync and async, whether it calls it that or not - or your IDE has an ad-hoc informally specified bug-ridden slow implementation of half of one.
This is very rarely necessary in app code. It's usually only a pattern for higher-level utility functions, and even then if you really wanted to you could unify implementations with generators.
Where do you see insanity like this?
If you were designing a language from the ground up, why would you implement a synchronous read operation? I would just assume that all code written in the language will treat async the way that Haskell programmers treat IO and make all IO operations async no matter what.
See https://news.ycombinator.com/item?id=41055328
This bifurcation is mostly a concern for the standard library. It’s not something you do this way in your regular application code.
"Expensive" is subjective and should be up to the programmer to decide.
And, it absolutely should not require creating two identical function definitions to get around the function coloring problem - that's completely indefensible.
> This is a good thing for programmers to easily see and know at the call site.
Yes, and the IDE can show you that information, exactly like it does the types of arguments. We don't redundantly write the types of arguments at the call site because it's a good thing for programmers to see (which it is), we let the IDE do that.