

Syntactic sugar for IO
source link: https://blog.ploeh.dk/2020/06/29/syntactic-sugar-for-io/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

How to make use of the C# IO container less ugly.
This article is part of an article series about the IO container in C#. In the previous article you saw a basic C# hello world program using IO<T>
to explicitly distinguish between pure functions and impure actions. The code wasn't as pretty as you could hope for. In this article, you'll see how to improve the aesthetics a bit.
The code isn't going to be perfect, but I think it'll be better.
Sugared version #
The IO<T>
container is an imitation of the Haskell IO
type. In Haskell, IO
is a monad. This isn't a monad tutorial, and I hope that you're able to read the article without a deep understanding of monads. I only mention this because when you compose monadic values with each other, you'll sometimes have to write some 'noisy' code - even in Haskell. To alleviate that pain, Haskell offers syntactic sugar in the form of so-called do
notation.
Likewise, F# comes with computation expressions, which also gives you syntactic sugar over monads.
C#, too, comes with syntactic sugar over monads. This is query syntax, but it's not as powerful as Haskell do
notation or F# computation expressions. It's powerful enough, though, to enable you to improve the Main
method from the previous article:
static IO<Unit> Main(string[] args) { return from _ in Console.WriteLine("What's your name?") from name in Console.ReadLine() from now in Clock.GetLocalTime() let greeting = Greeter.Greet(now, name) from res in Console.WriteLine(greeting) select res; }
If you use C# query syntax at all, you may think of it as exclusively the realm of object-relational mapping, but in fact it works for any monad. There's no data access going on here - just the interleaving of pure and impure code (in an impureim sandwich, even).
Infrastructure #
For the above code to compile, you must add a pair of methods to the IO<T>
API. You can write them as extension methods if you like, but here I've written them as instance methods on IO<T>
.
When you have multiple from
keywords in the same query expression, you must supply a particular overload of SelectMany
. This is an oddity of the implementation of the query syntax language feature in C#. You don't have to do anything similar to that in F# or Haskell.
public IO<TResult> SelectMany<U, TResult>(Func<T, IO<U>> k, Func<T, U, TResult> s) { return SelectMany(x => k(x).SelectMany(y => new IO<TResult>(() => s(x, y)))); }
Once you've implemented such overloads a couple of times, they're more tedious than challenging to write. They always follow the same template. First use SelectMany
with k
, and then SelectMany
again with s
. The only marginally stimulating part of the implementation is figuring out how to wrap the return value from s
.
You're also going to need Select
as shown in the article about IO as a functor.
Conclusion #
C#'s query syntax offers limited syntactic sugar over functors and monads. Compared with F# and Haskell, the syntax is odd and its functionality limited. The most galling lacuna is that you can't branch (e.g. use if
or switch
) inside query expressions.
The point of these articles is (still) not to endorse this style of programming. While the code I show in this article series is working C# code that runs and passes its tests, I'm pretending that all impure actions in C# return IO
results. To be clear, the Console
class this code interacts with isn't the Console class from the base class library. It's a class that pretends to be such a class from a parallel universe.
So far in these articles, you've seen how to compose impure actions with pure functions. What I haven't covered yet is the motivation for it all. We want the compiler to enforce the functional interaction law: a pure function shouldn't be able to invoke an impure action. That's the topic for the next article.
Recommend
-
44
README.md Then
-
8
Syntactic Sugar Is Not Always Good This write-up is partly inspired by a recent post by Vlad Mihalcea...
-
11
Ruby Magic Syntactic sugar methods in Ruby Tom de Bruijn on Feb 20, 2018 “I absolutely love AppSignal.” Discover AppSignal
-
7
Syntactic sugar in C - (ab)using "for" loopsThe for loop is one of the most powerful constructions in the C language.It consists of three different parts. The first one is initialization, performed exactly once at...
-
9
What is syntactic sugar? In programming, the term syntactic sugar is used to describe some syntax that is meant to make some part of a programming language easier to read and express. Syntactic sugar means that the syntax does not...
-
6
-
7
Why async/await is more than just syntactic sugar#javascriptPublished on 08 August, 2022My takes on async/await vs PromiseDespite thousands of posts on async/await vs....
-
1
August 13, 2022 On syntactic sugar Ever so often the term ‘syntactic sugar’ comes when people discuss language features, and it’s not uncommon to see the word ‘just’ right in front of it; some examples: T...
-
4
Syntactic sugar methods in Ruby
-
4
Oct 10th, 2022Syntactic Sugar, Declarative and First Class Citizens? What does that even mean?👇 Download Show
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK