{{ message }}

Instantly share code, notes, and snippets.

zudov/AffEventuallyPromises.md

Last active Jun 17, 2016
Aff vs. Promises vs. Eventual values.

I recently read this interesting post about eventual values. One thing that struck me and which I failed to understand is:

• Eventual Values can be interacted with like normal values.
• If an Eventual Value is part of a simple value operation, then that expression resolves to a new Eventual > Value which resolves when all its Eventual Values are resolved.

If I understood the author correctly, that is supposed to be solving several problems:

1. "I don’t know if this is a Promise or not" (I don't know if it's the resolved result of the action, or the action itself).
2. "I’d really like to write code that interacts with values, and not Promises, and leave the machinery to the computer to work out".

I don't see how (1) can be solved by "[making values that aren't yet resolved] mostly indistinguishable from a “normal” value". I don't think (2) is possible to resolve unambigiously, often there would be multiple ways to execute a sequence of actions and only few of them would be 'correct' (match application logic).

I hope I didn't take any quotes out of the context here. To see a full picture please refer to the original post.

In this gist I want to show my perspective and the solution that I am currently employing by implementing the examples given in the post using PureScript and `purescript-aff`.

Pure functions

Please meet these pure functions. They have nothing to do with async:

```addFive :: Int -> Int
addFive x = x + 5

addThreeInts :: Int -> Int -> Int -> Int
addThreeInts x y z = x + y + z```

`Aff` actions

That's an async action (indicated by `Aff` in the type signature). It would get a random integer (as a string) from random.org Then it would parse that string into an actual integer, and if the parsing fails it would return 42. The used combinators are explained further.

```getRandomInt :: Aff _ Int
getRandomInt = map (fromMaybe 42 <<< Int.fromString <<< _.response)
(Ajax.get url)
where
url = "https://www.random.org/integers/?num=1&min=1&max=6&col=1&base=10&format=plain"```

Some Combinators

map

`map` (aka `<\$>`) -- allows us to apply pure function to the (result of) `Aff` action.

We have an `Aff` action `getRandomInt` and a pure function `addFive`. We can combine them, simply by using `map` operator:

```addFiveToRandomInt :: Aff _ Int
`<<<` (backward function composition)

`<<<` is just a function composition.

```addTenToRandomInt :: Aff _ Int

Same but with operators

Using operators instead of normal functions, makes the thing more terse, but once you get used it reads like a piece of cake:

`getRandomInt = fromMaybe 42 <<< Int.fromString <<< _.response <\$> Ajax.get url`

Or if you don't like reading right-to-left:

`getRandomInt = Ajax.get url <#> _.response >>> Int.fromString >>> fromMaybe 42`

Applying a pure function to multiple arguments

You can apply a pure function to multiple actions using `liftN` family of functions:

```sumOfThreeRandomInts :: Aff _ Int
sumOfThreeRandomInts =
lift3
getRandomInt
getRandomInt```

There is also `apply` (aka `<*>`) combinator. Using it together with `<\$>` allows to do the same thing as with `liftN`, but scales to arbitrary amount of arguments and gives quite a nice pattern.

```sumOfThreeRandomInts :: Aff _ Int
sumOfThreeRandomInts =
<*> getRandomInt
<*> getRandomInt```

Parallelizing

Note that even though that code is asynchronous (e.g. `Ajax.get` won't block the main thread), `Aff` actions are sequential by default. `sumOfThreeRandomInts` would perform three requests sequntially even though they could be performed in parallel.

In order to parallelize those requests we need to use the `Par` helper:

```sumOfThreeRandomIntsPar :: Aff _ Int
sumOfThreeRandomIntsPar =
(Par getRandomInt)
(Par getRandomInt))```

Do syntax

There is also a handy `do` syntax. For this simple example it would be redundant since none of our actions depend on the result of the previous actions. Just to show it off:

```sumOfThreeRandomIntsUsingDo :: Aff _ Int
sumOfThreeRandomIntsUsingDo = do
x <- getRandomInt
y <- getRandomInt
z <- getRandomInt

Conclusion

• Asynchronous actions (`Aff`) are explicit on the type level. It is statically known, which of your values are `Aff` actions and which are just normal pure values.

The problem of "I don’t know if this is a Promise or not" simply doesn't exist.

For example in order to `addFive` to the result of `getRandomInt`, we have to explicitly use `map` combinator. If we don't the compiler would complain:

``````-- Add 5 to the result of `getRandomInt`

-- Add 5 to the `getRandomInt` action. Doesn't make any sense.
• Lots of well-defined combinators and `do`-notation allows you to combine your pure values and `Aff` actions without falling into hell like:
``````Promise.all([x, y, z]).then((ns) => Promise.resolve(ns[0] + ns[1] + ns[2])
Your code manipulates normal values and `Aff` actions (which are just a special type of values). At the boundaries you have to explicitly tell how to interleave them together. Computer can't unambigiously work that out, but it can check whether your usage of machinery makes sense. The idea of working that out automatically sounds a bit like lazy evaluation, and experience of using lazy IO in Haskell makes me a bit skeptical about that.
• `purescript-aff` is just a library. No `purescript-aff` specific magic is present in the compiler.