Obtaining the initial successful value within an array containing TaskEithers

As someone who is diving into functional programming and just beginning to explore fp-ts, I am grappling with understanding the util functions provided. My current challenge involves figuring out how to handle TaskEithers as fallbacks within an array.

I have a function that retrieves data for a specific id, returning either an Error or a Success:

declare function getData(id: number): TaskEither<Error, Success>

My goal is to create a function that loops through an array of ids (e.g. [1, 2, 3, 4]), fetching data for each one. It should stop at the first successful TaskEither and return Right<Success>. If all TaskEithers fail, it should aggregate their errors into a Left<Error[]>.

import { map } from 'fp-ts/lib/Array';

const program: TaskEither<Error[], Success>
  = pipe(
    [1, 2, 3, 4],
    map(getData),
    /*
     * Now I have a TaskEither<Error, Success>[]
     * What comes next?
     */
  );

I attempted a similar approach, but encountered some clear issues (outlined below):

import { map, sequence } from 'fp-ts/lib/Array';
import { map as mapTaskEither } from 'fp-ts/lib/TaskEither'

const program: TaskEither<Error, Success>
  = pipe(
    [1, 2, 3, 4],
    map(getData),
    sequence(taskEither), // This now results in a TaskEither<Error, Success[]>
    mapTaskEither(successes => successes[0])
  );

Challenges with this method:

  1. It executes getData for all IDs without stopping at the first success
  2. If any of the getData calls encounter an error, the overall program will error out, even if previous calls were successful
  3. The errors are not accumulated into an array of Error[]

Answer №1

Big thanks to the amazing insight provided in this comment. I have found a solution thanks to it.

For better readability, it's helpful to assign a name to this utility function:

import { TaskEither, taskEither, swap } from 'fp-ts/lib/TaskEither';
import { sequence, map } from 'fp-ts/lib/Array';
import { pipe } from 'fp-ts/lib/function';

const firstSuccessOrErrors: <A, B>(taskEithers: TaskEither<A, B>[]) => TaskEither<A[], B>
    = taskEithers => pipe(
        taskEithers,
        map(swap),
        sequence(taskEither),
        swap,
    );

This function can be used as follows:

import { map } from 'fp-ts/lib/Array';

const program: TaskEither<Error, Success>
  = pipe(
    [1, 2, 3, 4],
    map(getData),
    getFirstRightOrLefts, // <-- Exciting!
  );

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Restricting types does not appear to be effective when it comes to properties that are related

I am working with a specific type that looks like this: type Props = { type: 'foo'; value: string; } | { type: 'baz'; value: number; }; However, when using a switch statement with the type property in TypeScript, the program in ...

Setting options using the form group in dropdowns in Angular can greatly enhance the user experience

I have created a FormGroup called holidayform and set it up as follows: holidayform: FormGroup; this.holidayform = this.fb.group({ title: ['', [Validators.required]], entryDate: ['',], }) this.holidayform.patchValue ...

Why is my Typescript event preventDefault function ineffective?

Despite all my efforts, I am still unable to prevent the following a tag from refreshing the page every time it's clicked. <p> <a onClick={(e) => handleClick} href="&qu ...

I am encountering an issue where the nested loop in Angular TypeScript is failing to return

I am facing an issue with my nested loop inside a function. The problem is that it is only returning the default value of false, instead of the value calculated within the loop. Can someone please point out what I might be doing incorrectly? Provided belo ...

Exploring the process of retrieving token expiration in TypeScript using the 'jsonwebtoken' library

Currently utilizing jsonwebtoken for token decoding purposes and attempting to retrieve the expiration date. Encountering TypeScript errors related to the exp property, unsure of the solution: import jwt from 'jsonwebtoken' const tokenBase64 = ...

Hiding the line connector between data points in ChartJs

I recently took over a project that includes a line chart created using Chart.js by the previous developer. My client has requested that I do not display a line between the last two data points. Is this possible with Chart.js? I have looked through the doc ...

Utilizing TypeScript decorators for Node.js authentication and authorization

In the process of developing a server-side Node.js application using TypeScript, I have come to the point where I need to implement authorization. My initial plan was to utilize TypeScript "Method and Class Decorators" for this purpose. However, I came acr ...

I'm looking to retrieve the Response from a POST request, which happens to be an array of SearchSample objects in Angular

My Component's Search Function: searchWithNyckel(){ const formData = new FormData(); formData.append('image', this.updateFormGroup.get('updateProduct.image').value); this.productService.searchProductNyckel(formData).subscribe( ...

Loop through an array of objects using Typescript

Currently, I am facing a challenge in Angular 2 where I have an array of objects that I need to iterate over. However, I also need to limit the display length of a specific key's string value within each object. this.productService.loadAllProducts(pro ...

Using TypeScript, a parameter is required only if another parameter is passed, and this rule applies multiple

I'm working on a concept of a distributed union type where passing one key makes other keys required. interface BaseArgs { title: string } interface FuncPagerArgs { enablePager: true limit: number count: number } type FuncArgs = (Fu ...

Why bother specifying types when extending tsconfig?

Having an angular app that utilizes @types and custom typings, I am facing an issue where the app works when served but encounters errors during testing with ng test. It is puzzling to me why this discrepancy exists, and I am struggling to comprehend the r ...

angular2 with selenium webdriver: Issue with resolving 'child_process' conflict

I followed these steps: ng new typescript-selenium-example npm install selenium-webdriver --save I also made sure to copy chromedriver to my /Application. I updated the app.component.ts file as shown below: import { Component } from '@angular/core ...

Using the ngrx signalStore within the facade design pattern - a step-by-step guide

How can I utilize ngrx's new signalStore in Angular to fetch locations of arms, save them in the state, and replace a service with LOCATION_STORE after setting the locations on a map with markers? The challenge lies in waiting for the response of loca ...

Maintain a variable within the scope of _.forEach() that is separate from the array being iterated

Occasionally, I encounter a scenario where objects need to be pushed into a separate array based on the content of a looped array: let customArray: any[]; _.forEach(iteratedArray, (item:any) => { // some irrelevant code... customArray.push(item ...

Rejecting the request by clicking a button

While switching to a menu, I retrieve data from the API. As the pending data is still loading in DevTools, I click on the Filter button to search for additional data. The data I am searching for appears in my table. However, once the pending data finishes ...

Navigating through Interfaces in Typescript with Union Types

When faced with a scenario where you have two interfaces that are similar enough to be processed by the same logic, what is the most effective approach to take: interface DescriptionItem { Description: string; Code: string; } interface NamedItem { ...

The Angular 2 and TypeScript error stating that the argument of type response is not assignable is causing issues

In my Angular application, I have a service that includes a value defined as: private client_comments = new BehaviorSubject([]); When attempting to update this value with the response from an HTTP request, I encountered the following error message: A ...

Debugging in Next.js and Sanity.io: "Client Components do not support async/await yet, only Server Components."

I am a beginner working on creating a website and e-commerce store using React, Next 14, Typescript, and Sanity as the CMS. I have been following a tutorial available at https://www.youtube.com/watch?v=g2sE034SGjw&t. Encountering the following error: ...

Utilizing winston to generate multiple log files with set maximum sizes and daily rotation

Currently, I am utilizing winston for logging with a maximum size and daily rotation. I am interested in having this functionality with one file per API endpoint to define multiple log files. Is there a way to achieve this? Displayed below is my winston ...

Issue with vue-class-component: encountering TS2339 error when trying to call a method within

My vuejs application is being built using vue-cli-service. After a successful build, I encountered TS2339 errors in my webstorm IDE: Test.vue: <template> <div>{{method()}}</div> </template> <script lang="ts"> impor ...