fp-ts icon indicating copy to clipboard operation
fp-ts copied to clipboard

Either.Do: `left` to be structurally similar to `right`

Open v0rs4 opened this issue 1 year ago • 1 comments

I've come across certain challenge with Either.DO

Here's a working example

import * as A from 'fp-ts/Apply'
import * as E from 'fp-ts/Either'
import { pipe } from 'fp-ts/function'
import * as S from 'fp-ts/Semigroup'
import * as string from 'fp-ts/string'
import * as RR from 'fp-ts/ReadonlyRecord';

const parseString = (u: unknown): E.Either<string, string> =>
  typeof u === 'string' ? E.right(u) : E.left('not a string')

const parseNumber = (u: unknown): E.Either<string, number> =>
  typeof u === 'number' ? E.right(u) : E.left('not a number')

interface Person {
  readonly name: string
  readonly age: number
}

(() => {
  const apS = A.apS(E.getApplicativeValidation(RR.getUnionSemigroup(pipe(string.Semigroup, S.intercalate(', ')))))

  const lift = <E, A, B, C extends string>(action: (a: B) => E.Either<E, A>, name: C): (a: B) => E.Either<RR.ReadonlyRecord<string, E>, A> => (a) => pipe(action(a), E.mapLeft((e) => ({ [name]: e })));

  const parsePerson = (input: Record<string, unknown>) =>
    pipe(
      E.Do,
      apS('name',lift(parseString, "name" as const)(input.name)),
      apS('age', lift(parseNumber, "age" as const)(input.age))
    )

  console.log(parsePerson({}))
})();

So, the return type of parsePerson(...) is

const parsePerson: (input: Record<string, unknown>) => E.Either<Readonly<Record<string, string>>, {
    readonly name: string;
    readonly age: number;
}>

...whille I'm trying to make it look like this

const parsePerson: (input: Record<string, unknown>) => E.Either<
    readonly name: string;
    readonly age: string;
>, {
    readonly name: string;
    readonly age: number;
}>

v0rs4 avatar Aug 01 '23 19:08 v0rs4

You can't get that result from that input because it doesn't encode for fallibility, for example what if there's no name key at all?

samhh avatar Aug 02 '23 11:08 samhh