javascripttypescriptfunctional-programmingfp-ts

Problem on TaskEither sequenceArray function for processing different types of return type


I am working on functional programming using fp-ts library. Here is my code and my problem:

import * as TE from 'fp-ts/TaskEither';
import { pipe } from 'fp-ts/lib/function';

function getName(): TE.TaskEither<Error, String> {
  return TE.right('John Doe');
}

function getAge(): TE.TaskEither<Error, number> {
  return TE.right(40);
}

export function seqArrayTest(): TE.TaskEither<Error, string> {
  return pipe(
    TE.sequenceArray([
         getName(), 
         getAge(),
       ]),
    TE.chain(([name, age]) => {
      return TE.right(`Name: ${name}, Age: ${age}`);
    }),
  );
}

Here:

  1. I have two functions: getName and getAge returning TaskEither with different value types.
  2. I am calling them parallel using TE.sequenceArray.
  3. After the computation, I am just chaining the response and returning another TaskEither.

During compilation, on TE.sequenceArray([getName(), getAge()]), it shows below error for getAge():

Type 'TaskEither<Error, number>' is not assignable to type 'TaskEither<Error, String>'. Type 'number' is not assignable to type 'String'.ts(2322)

But my understanding is: TE.sequenceArray should take an array of functions with different return types that are wrapped with TaskEither and it will return an array of responses sequentially.


Solution

  • @Souperman answer is correct in this context.TE.sequenceArray expects an array of TaskEithers with the same Right types.

    If we want to process an array of TaskEither and the return type is different, then we may use sequenceT.

    Here is the modified code:

    import * as TE from "fp-ts/TaskEither";
    import { pipe } from "fp-ts/lib/function";
    import { sequenceT } from "fp-ts/Apply";
    
    function getName(): TE.TaskEither<Error, String> {
      return TE.right("John Doe");
    }
    
    function getAge(): TE.TaskEither<Error, number> {
      return TE.right(30);
    }
    
    export function seqArrayTest(): TE.TaskEither<Error, string> {
      return pipe(
        sequenceT(TE.ApplyPar)(getName(), getAge()),
        TE.chain(([name, age]) => {
          return TE.right(`Name: ${name}, Age: ${age}`);
        }),
        TE.mapLeft((e) => e)
      );
    }
    

    Here, TE.ApplyPar: Runs computations in parallel. If we want sequential, then we may use TE.ApplySeq.

    In TE.chain(([name, age]) parameter name is a string type, and age is a number type. And response sequence will based on the calling sequence.