2

I have the following code:

import { Task, task } from "fp-ts/lib/Task"
import { Either, left, right } from "fp-ts/lib/Either"
import { curry } from "fp-ts/lib/function"
import { liftA2 } from "fp-ts/lib/Apply"
import { Repo } from "./repo"

const buildPerson = curry((name: string, age: number): Either<Error, any> => {
    if (name !== undefined && age !== undefined) {
        return right({ name, age })
    } else {
        return left(Error("Missing parameter"))
    }
})

const validatePerson = (person: any): Either<Error, any> => {
    if ( person.age < 18) {
        return left(Error("Too Young")) 
    } else {
        return right(person)
    }
}

const getNameFromRepo = (repo: Repo): Task<string> => {
    return new Task(
        () => repo.getName()
    )
}

const getAgeFromRepo = (repo: Repo): Task<number> => {
    return new Task(
        () => repo.getAge()
    )
}

const savePerson = curry((person:any, repo: Repo): Task<void> => {
    return new Task(
        () => {
            console.log(person)
            return repo.setPerson(person)
        }
    )
})

const hello = async () => {
    const repo = new Repo()

    await liftA2(task)(buildPerson)(getNameFromRepo(repo))(getAgeFromRepo(repo))
    .map(
        (e) => e.chain(
            (p) => validatePerson(p)
        )
    )
    .map(
        (e) => e.fold(
            (l) => console.log(l),
            (r) => savePerson(r)
        )
    )
    .run()
}

hello()

1) savePerson function doesn't get run, return type however is Promise

2) Fp-Ts library indicates that liftA2 type is deprecated and I should use sequenceT instead. However from the signature it's not clear how sequenceT would apply to the parameters of buildPerson like liftA2 does

3) Is there a better way to compose the functions?

  • I love this type of coding, but I haven't really found a way to incorporate it for general consumption. Is this practice coding or real use? – james emanon May 18 '19 at 21:32
  • Your `(e) => e.fold(l => console.log(l), r => savePerson(r))` function does return either `undefined` or a `Task`. The task is never run, since you used the function as a callback to `map` - if you wanted to chain tasks, you'd need to use `chain`. (And wrap the `console.log` call in a task as well). – Bergi May 19 '19 at 14:57

1 Answers1

0

This complication is caused by needing to go between either and task and there not being a natural transformation between the two types. Why not use the existing taksEither type? fp ts has very poor docs so I rewrote your code using a different library's taskeither (from fluture js)

Instead heres how this would look using fluturejs (essentially task + either)

import * as Future from 'fluture';

const buildPerson = (name, age) =>
    name !== undefined && age !== undefined
        ? Future.of({ name, age })
        : Future.reject(Error("Missing parameter"));

const validatePerson = ({ name, age }) =>
    age < 18
        ? Future.reject(Error("Too Young"))
        : Future.of({ name, age });

const getNameFromRepo = (repo: Repo) => {
    return Future.Future((reject, resolve) =>
        resolve(repo.getName()));
}

const getAgeFromRepo = (repo: Repo) => {
    return Future.Future((reject, resolve) =>
        resolve(repo.getAge()));
}

const savePerson = (repo: Repo) => (person) => {
    return Future.Future((reject, resolve) =>
        resolve(repo.setPerson(person)));
}
const hello = (repo: Repo) =>
    Future.parallel(1, [
        getNameFromRepo(repo),
        getAgeFromRepo(repo),
    ])
        .chain(([name, age]) => buildPerson(name, age))
        .chain(validatePerson)
        .chain(savePerson(repo))
        .fork(
            (err) => console.warn('err', err),
            () => console.log('it worked!'),
        );

hello(new Repo());

I wasnt able to scan the docs and figure out what these functions are called in fp-ts's implementation but Im sure they all exist. Flutures parallel function takes a list of Futures and returns a future of a list, this behavior is essentially concat and should exist in fp-ts's taskeither because its required to be implemented on monads

Futhermore, if you redesigned the repo class and have its methods return taskeithers, you will further simplify your code.

Alfred Young
  • 397
  • 1
  • 3
  • 15