Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Perform side effect and return original input (tee) #1039

Closed
abacaj opened this issue Dec 5, 2019 · 10 comments
Closed

Perform side effect and return original input (tee) #1039

abacaj opened this issue Dec 5, 2019 · 10 comments

Comments

@abacaj
Copy link

abacaj commented Dec 5, 2019

馃摉 Documentation

Does fp-ts have a concept of a tee example: https://davefancher.com/2015/01/11/extending-f-pipelines-with-a-tee-function/

鈥済iven a value, apply a function to it, ignore the result, then return the original value."

I've got a use case for TaskEither to perform some uploading and return the original input to the next pipeline function.

@abacaj
Copy link
Author

abacaj commented Dec 5, 2019

example use case:

pipe(
  fileUrls,
  TaskEither.tee(upload),
  TaskEither.chain(parseCsv)
)

@abacaj
Copy link
Author

abacaj commented Dec 5, 2019

This seems to work for my use case:

export const taskEitherEffect = <E, A>(f: (a: A) => TaskEither<E, void>) => (b: TaskEither<E, A>) => {
    return pipe(
        b,
        TE.fold((e) => {
            return TE.left(e);
        }, (value) => {
            return pipe(
                f(value),
                TE.map(() => value)
            );
        })
    )
}

usage:

pipe(
  fileUrls,
  taskEitherEffect(upload)
  TaskEither.chain(parseCsv)
)

Upload just returns a promise with an either (taskeither) error and void

upload: (fileUrls: string[]) => TaskEither<Error, void>

The parseCsv now has access to the original input (fileUrls) and if the upload fails we pass the error along instead of the original input.

@gcanti
Copy link
Owner

gcanti commented Dec 5, 2019

You may want chainFirst

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

declare const fileUrls: Array<string>
declare function upload(fileUrls: Array<string>): TE.TaskEither<Error, void>
declare function parseCsv(fileUrls: Array<string>): TE.TaskEither<Error, void>

pipe(TE.right(fileUrls), TE.chainFirst(upload), TE.chain(parseCsv))

@gkamperis
Copy link

@gcanti would be great to add this to the docs and a recipes/how to page...

@abacaj
Copy link
Author

abacaj commented Dec 5, 2019

You may want chainFirst

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

declare const fileUrls: Array<string>
declare function upload(fileUrls: Array<string>): TE.TaskEither<Error, void>
declare function parseCsv(fileUrls: Array<string>): TE.TaskEither<Error, void>

pipe(TE.right(fileUrls), TE.chainFirst(upload), TE.chain(parseCsv))

Yep, that worked - it's a direct replacement for my implementation. Thanks!

@gcanti gcanti closed this as completed Dec 6, 2019
@gcanti
Copy link
Owner

gcanti commented Dec 6, 2019

@gkamperis any contribution to the documentation is welcomed!

@gkamperis
Copy link

@gcanti of course...
however I do not feel I have enough knowledge to create these sort of examples and I do not know if this page already exists or where it should live.

Is there an example I can look at?

@JohnForster
Copy link

Just a quick question on this, what would be the correct way to implement this if you also didn't care if the upload failed or not? I'd like to be able to discard the Error state as well. Is there a better way to implement this?

@mohaalak
Copy link
Contributor

mohaalak commented Mar 5, 2021

Just a quick question on this, what would be the correct way to implement this if you also didn't care if the upload failed or not? I'd like to be able to discard the Error state as well. Is there a better way to implement this?

I think you can use TaskEither.getOrElse to get a Task

@gcanti
Copy link
Owner

gcanti commented Mar 5, 2021

you could define a discard utility:

const discard: <E>(ma: TE.TaskEither<E, void>) => TE.TaskEither<E, void> = TE.alt(() =>
  TE.right(undefined as void)
)

const result = pipe(TE.right(fileUrls), TE.chainFirst(flow(upload, discard)), TE.chain(parseCsv))

in fp-ts@2.10 (<= currently a release candidate) there's a chainFirstTaskK utility:

const result = pipe(TE.right(fileUrls), TE.chainFirstTaskK(upload), TE.chain(parseCsv))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants