So far we've looked at a couple of things:
-
Option
andEither
which are both functors and monads -
They've helped us express stuff that goes wrong, as life does
-
Lots of different monoids for
strings
and things -
These have helped us formalise the smashing together of data
All of the abstractions we've looked at so far have a few things in common:
-
They are
pure
-
They are
synchronous
-
But surely you must find yourself asking...
-
"That's all great, but where is the business value hiding?"
-
Today we're going to hold onto
purity
like our life depends on it -
...but throw
synchronous
out of the window and straight through the sunroof of some idiot's passing sportscar
...a meandering distraction.
So far, we've not been concerned about time. Pure functions are handy like that. They simplify down like maths equations.
-
a = (6 * 3) + (4 + 2)
-
a = 18 + (4 + 2)
-
a = 18 + 6
-
a = 24
-
OK
If we look at the above as a bunch of functions though, it's more apparent there is some sort of ordering to how things are calculated.
a = add(multiply(6,3), add(4, 2))
-
The outer
add
function adds numbers, not functions, therefore, it can't do anything. -
But
multiply
deals with numbers, and it has those, so let's do that:
a = add(18, add(4,2))
- We still can't add
18
to a function, but we can calculate the inneradd
:
a = add(18, 6)
- And now the outer function is ready to go brrrrrrrrrrr:
a = 24
Let's look at the functions we've been using a lot of recently:
map :: (A -> B) -> Option A -> Option B
- (brief recap)
const map = <A, B>(f: (a: A) => B, a: Option<A>): Option<B> =>
a.type === 'Some' ? some(f(a.value)) : none()
- Ordering doesn't really matter here, all the values are ready to go
Let's imagine a fictional type Async<A>
.
-
Think of it like a
Promise<A>
, ie something that will give me an A at some point -
How does
map
work ordering wise there?
map :: (A -> B) -> Async A -> Async B
-
What's going to happen, roughly, step by step?
-
.
-
..
-
...
What about bind
?
bind :: (A -> Async B) -> Async A -> Async B`
-
What's going to happen? How long is it going to take?
-
.
-
..
-
...
Meet our new buddy ap
. Conceptually, he lives somewhere between map
and bind
.
- He works on a bunch of our old friends:
ap :: Option (A -> B) -> Option A -> Option B
ap :: Either E (A -> B) -> Either E A -> Either E B
-
He can be quite helpful there, but not particularly interesting.
-
However...
-
Today though, we'll look at him with our new
Async
friend:
ap :: Async (A -> B) -> Async A -> Async B
-
We wait for an
A -> B
function from our firstAsync
-
We also wait for an
A
value from our second one. -
Then we smash them together.
-
What's different and useful about this?
-
What if each of our
Async
s were doing long slow API calls?
Today we're going to build a thing called Continuation
(in some places it's
called Cont
)
It's better than a callback, worse than a Promise.
We can improve him in future though.