3 ms·
Some F# articles are outdated - they predate F# 6 which added task { } CE which simplifies asynchronous code that interacts with .NET's standard library. I fou
by neonsunset 2y ago
Some F# articles are outdated - they predate F# 6 which added task { } CE which simplifies asynchronous code that interacts with .NET's standard library.
I found F# to be more pleasant to work with async than C# (which is already a breeze). It is true that you still have to define 'task' (or 'async' if you want to use Async CEs) but it is generally there for a reason. I don't think it's too much noise:
let printAfter (s: float<second>) = task {
let time = TimeSpan.FromSeconds (float s)
do! Task.Delay time
printfn $"Hello from F# after {s} seconds"
}
- shortrounddev2 2y agoI dislike that there's a kind of sub-syntax specifically for async. I like how C# converts `await` into the necessary calls. In this code I think it would look better to simply have: let async printAfter (s: float<second>) = let time = TimeSpan.FromSeconds (float s) await Task.Delay time printfn $"Hello from F# after {s} seconds" and then printAfter is called with `await` as well. I'm sure there's some FP kind of philosophy which prohibits this (code with potential side effects not being properly quarantined), but to me it just results in yet more purpose-specific syntax to have to learn for F#, which is already very heavy on the number of keywords and operators
- neonsunset 2y agoIt's the other way around. 'async'-annotated methods in C# enable 'await'ing on task-shaped types. It is bespoke and async-specific. There is nothing wrong with it but it's necessary to acknowledge this limitation. let!, and!, return!, etc. keywords in F# are generic - you can build your own state machines/coroutines with resumable code, you can author completely custom logic with CEs. I'm not sure what led you to believe the opposite. `await Task.Delay` in C# is `do! Task.Delay` in F#. `let! response = http.SendAsync` is for asynchronous calls that return a value rather than unit. In the same vein, seq is another CE that is more capable than iterator methods with yield return: let values = seq { // yield individual values for i in 1..10 -> i // yield a range, merged into the sequence yield! [11..20] // note the exclamation mark } Adding support for this in C# would require explicit compiler changes. CEs are generic and very powerful at building execution blocks with fine control over the behavior, DSLs and more. Reference: https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/computation-expressions https://learn.microsoft.com/en-us/dotnet/fsharp/language-ref...
- shortrounddev2 2y agoI'm somewhat already aware of these considerations, it's just that when you're working in web development, a huge amount of your code is async and this means that a large part of the code is wrapped up in these computation expressions that I think are just plain ugly
- miffy900 2y agoWhat's so ugly about them? I don't code in F#, but I do in C# and after reading about them I wish C# had something similar.
- shakna 2y ago> It is bespoke and async-specific. There is nothing wrong with it but it's necessary to acknowledge this limitation. I would disagree. If you need to have a bespoke set of syntax, then something is not integrated where it should be. The language design should not be such that you are writing things differently, depending on the paradigm that you're handling. That's not something that occurs in every language, so it isn't essential that it exists. We can acknowledge the differences in a way that alerts the programmer, without forcing the programmer to switch syntaxes back and forth when moving between the paradigms. async/await is one method, Promises another, etc. A different syntax is a much, much higher cognitive load.
- neonsunset 2y agoI cannot make sense of this reply. Different languages have different syntax. Support of asynchronous code and of its composition is central to C#, which is why it does it via async/await and Task<T> (and other Task-shaped types). Many other languages considered this important enough to adopt a similar structure to their own rendition of concurrency primitives, inspired by C# either directly or indirectly. Feel free to take issue with the designers of these languages if you have to. F#, where async originates from, just happens to be more "powerful" as befits an FP language, where resumable code and CEs enable expressing async in a more generalized fashion. I'm not sold on idea that C# needs CEs. It already has sufficient complexity and good balance of expressiveness.