Fp-ts: [RFC] pipe-based Do

Created on 7 Jul 2020  路  9Comments  路  Source: gcanti/fp-ts

Looks like people are quite happy with fp-ts-contrib's Do, it would be nice having a pipe-based Do though, here's a POC

import { pipe } from 'fp-ts/lib/function'
import { URIS2, Kind2 } from 'fp-ts/lib/HKT'

export interface Do2<M extends URIS2> {
  readonly as: <N extends string>(name: N) => <E, A>(fa: Kind2<M, E, A>) => Kind2<M, E, { [K in N]: A }>
  readonly let: <N extends string, A, B>(
    name: Exclude<N, keyof A>,
    f: (a: A) => B
  ) => <E>(fa: Kind2<M, E, A>) => Kind2<M, E, { [K in keyof A | N]: K extends keyof A ? A[K] : B }>
  readonly bind: <N extends string, A, E, B>(
    name: Exclude<N, keyof A>,
    f: (a: A) => Kind2<M, E, B>
  ) => (fa: Kind2<M, E, A>) => Kind2<M, E, { [K in keyof A | N]: K extends keyof A ? A[K] : B }>
}

//
// example with `Either`
//

import * as E from 'fp-ts/lib/Either'
declare const Do: Do2<E.URI>

/*
const x: E.Either<string, {
    a: number;
    b: string;
    c: boolean;
}>
*/
export const x = pipe(
  E.right<string, number>(1),
  Do.as('a'),
  Do.bind('b', () => E.right('b')),
  Do.let('c', ({ a, b }) => a + b.length > 0)
)

Pros:

  • do / doL are replaced by chainFirst
  • return is map
  • done is useless (would be the identity function)

All 9 comments

Looks good, but is bindW allowed?

Being pipe-based we (and users too) are free to add more functions (on data type basis)

export interface BindW2<M extends URIS2> {
  readonly bindW: <N extends string, A, D, B>(
    name: Exclude<N, keyof A>,
    f: (a: A) => Kind2<M, D, B>
  ) => <E>(fa: Kind2<M, E, A>) => Kind2<M, D | E, { [K in keyof A | N]: K extends keyof A ? A[K] : B }>
}

declare const Do: Do2<E.URI> & BindW2<E.URI>

/*
const x: E.Either<string | number, {
    a: number;
    b: string;
    c: boolean;
    d: number;
}>
*/
export const x = pipe(
  E.right<string, number>(1),
  Do.as('a'),
  Do.bind('b', () => E.right('b')),
  Do.let('c', ({ a, b }) => a + b.length > 0),
  Do.bindW('d', () => E.right<number, number>(2))
)

Love it! Where do I send the flowers? 馃尫 馃

Open question: as, bind, let, bindW etc... should be exported from Either.ts as a namespace (for example Do) or as top level functions?

Open question: as, bind, let, bindW etc... should be exported from Either.ts as a namespace (for example Do) or as top level functions?

@gcanti - If I understand correctly, you are asking about this

export const x = pipe(
  E.right<string, number>(1),
  E.as('a'),
  E.bind('b', () => E.right('b')),
  E.let('c', ({ a, b }) => a + b.length > 0),
  E.bindW('d', () => E.right<number, number>(2))
)

versus this

export const x = pipe(
  E.right<string, number>(1),
  E.Do.as('a'),
  E.Do.bind('b', () => E.right('b')),
  E.Do.let('c', ({ a, b }) => a + b.length > 0),
  E.Do.bindW('d', () => E.right<number, number>(2))
)

Am I correct?

If so, I would favor exporting as top-level functions since I find the former more readable (reduces clutter in the piped function calls). Just my preference though.

I would favor exporting as top-level functions

@IMax153 agree, I would rename as to bindTo though.

I'd start from adding

  • bindTo
  • bind
  • bindW (when possible)

to all monadic data types.

Hmm... I started experimenting with this and I get the following error when exporting let as a top-level function:

Identifier expected. 'let' is a reserved word in strict mode. Modules are automatically in strict mode.

Do you have a preference on another name for let?

@IMax153 PR here https://github.com/gcanti/fp-ts/pull/1283

Do you have a preference on another name for let?

Maybe we can live without it

export const x = pipe(
  E.right<string, number>(1),
  E.bindTo('a'),
  E.bind('b', () => E.right('b')),
  E.bind('c', ({ a, b }) => E.right(b.length > 0)), // <= this was a `let`
  E.bindW('d', () => E.right<number, number>(2))
)
Was this page helpful?
0 / 5 - 0 ratings

Related issues

mohsensaremi picture mohsensaremi  路  3Comments

Crashthatch picture Crashthatch  路  4Comments

FruitieX picture FruitieX  路  3Comments

mustafaekim picture mustafaekim  路  4Comments

mattgrande picture mattgrande  路  3Comments