fp-ts, sequenceT, and sweet sweet async typed FP
Tags: fpts, typescript, iots, functional
Published: 2019-08-31 16:56:20

---
title: fp-ts, sequenceT, and sweet sweet async typed FP
published: true
description: 
tags: fp-ts, typescript, io-ts, functional
---

Recently I found the need to make a set of async calls with different return types. This is a pretty common task, we'd like to make some calls in parallel and collect the results once everything is done. Let's take a look at the docs for [fp-ts async tasks](https://gcanti.github.io/fp-ts/recipes/async.html). 

```javascript
const tasks = [task.of(1), task.of(2)]
array
  .sequence(task)(tasks)()
  .then(console.log) // [ 1, 2 ]
```

Hmm. Well that's pretty nice, but what happens when the types are different?
```javascript
const tasks = [T.task.of(1), T.task.of("hello")]
array
  .sequence(task)(tasks)()
  .then(console.log) // [1, "hello"] I hope?
```

Uh oh.
![Sequence Type Error](https://thepracticaldev.s3.amazonaws.com/i/br4m0uqjptqvrdjg6xu9.png)  



Well darn. The type of `sequence` is (simplified) `Array[F[A]] => F[Array[A]]`, so all the return types would have to be the same.

What do? :/

After some googleing, I ran across the magical [sequenceT](https://github.com/gcanti/fp-ts/blob/bfc821e9e12ac36abb0a7adbceb8580d9e557448/src/Apply.ts#L75). 
```javascript
 /** 
 * const sequenceTOption = sequenceT(option)
 * assert.deepStrictEqual(sequenceTOption(some(1)), some([1]))
 * assert.deepStrictEqual(sequenceTOption(some(1), some('2')), some([1, '2']))
 * assert.deepStrictEqual(sequenceTOption(some(1), some('2'), none), none)
 */
```
Nice! Ok, let's try it out.

```javascript
import * as T from 'fp-ts/lib/Task'
import { sequenceT } from 'fp-ts/lib/Apply'
import { pipe } from 'fp-ts/lib/pipeable'

pipe(
  sequenceT(T.task)(T.of(42), T.of("tim")), //[F[A], F[B]] => F[A, B] 
  T.map(([answer, name]) => console.log(`Hello ${name}! The answer you're looking for is ${answer}`))
)();
```
```
Hello tim! The answer you're looking for is 42
```
Well that's rad. `pipe` allows us to chain calls together, so the result of `sequenceT` is passed in to `T.map`. `T.map` destructures the tuple and we can do as we please with some guarantees about our data. But what if our Tasks can fail?
```javascript
pipe(
  sequenceT(TE.taskEither)(TE.left("no bad"), TE.right("tim")),
  TE.map(([answer, name]) => console.log(`Hello ${name}! The answer you're looking for is ${answer}`)),
  TE.mapLeft(console.error)
)();
```
```
no bad
```
Awesome! Ok, time to get fancy with it. What if we are actually making some calls to an API, and we want to ensure that the results we get from the API conform to the expected schema?

Let's try it out by hitting a dummy REST endpoint with `axios`, which is a handy http client.

```javascript
import { array } from 'fp-ts/lib/Array'
import axios, { AxiosResponse } from 'axios';
import * as t from 'io-ts'

//create a schema to load our user data into
const users = t.type({
  data: t.array(t.type({
    first_name: t.string
  }))
});

//schema to hold the deepest of answers
const answer = t.type({
  ans: t.number
});

//Convert our api call to a TaskEither
const httpGet = (url:string) => TE.tryCatch<Error, AxiosResponse>(
  () => axios.get(url),
  reason => new Error(String(reason))
)

/**
 * Make our api call, pull out the data section and decode it
 * We need to massage the Error type, since `decode` returns a list of `ValidationError`s
 * We should probably use `reporter` to make this nicely readable down the line
 */
const getUser = pipe(
  httpGet('https://reqres.in/api/users?page=1'),
  TE.map(x => x.data),
  TE.chain((str) => pipe(
    users.decode(str), 
    E.mapLeft(err => new Error(String(err))), 
    TE.fromEither)
  )
);

const getAnswer = pipe(
  TE.right(42),
  TE.chain(ans => pipe(
    answer.decode({ans}), 
    E.mapLeft(err => new Error(String(err))), 
    TE.fromEither)
  )
)

/**
 * Make our calls, and iterate over the data we get back
 */
pipe(
  sequenceT(TE.taskEither)(getAnswer, getUser),
  TE.map(([answer, users]) => array.map(users.data, (user) => console.log(`Hello ${user.first_name}! The answer you're looking for is ${answer.ans}`))),
  TE.mapLeft(console.error)
)();
```
```
Hello George! The answer you're looking for is 42
Hello Janet! The answer you're looking for is 42
Hello Emma! The answer you're looking for is 42
Hello Eve! The answer you're looking for is 42
Hello Charles! The answer you're looking for is 42
Hello Tracey! The answer you're looking for is 42
```
Heck yeah! We did it! Async Typed FP for everyone! :)