如何使用任务monad? (fp-ts)

How to use the Task monad? (fp-ts)

import * as T from 'fp-ts/lib/Task'
import { pipe, flow } from 'fp-ts/lib/function'

const getHello: T.Task<string> = () => new Promise((resolve) => {
  resolve('hello')
})

我理解 Task 的目的及其重要性。问题是我真的不知道如何正确使用它或与它组合。

如果我只调用 getHello(),它会给我 Promise<pending>:

console.log(getHello()) // returns Promise<pending>

如果我这样做,但是:

const run = async () => {
  const hello = await getHello()
  console.log(hello) // prints 'hello'
}

有效。

但是这个:

const waitAndGet = async () => {
  return await getHello()
}

console.log(waitAndGet()) // prints Promise<pending>

没有。

此外,我将如何与它作曲?像这样:

const getHelloAndAddWorld = flow(
  getHello(),
  addAtEnd('world')
)

首先,让我们了解一下 Task 到底是什么。

export interface Task<A> {
  (): Promise<A>
}

// note that this could also be written as
export type Task<A> = () => Promise<A>

A Task 只是一个 return 是 Promise 的函数,因此在您的示例中调用 getHello 将 return 是 Promise<string> .

console.log(getHello())console.log(Promise.resolve('hello')) 相同,所以这就是为什么它会记录 Promise {<fulfilled>: "hello"}Promise<pending> 或其他内容而不是 hello 的原因]:

// Promise.resolve(foo) is the same as new Promise(resolve => resolve(foo))
const getHello = () => Promise.resolve('hello')
console.log(getHello())

有关 promises 的更多信息,我建议阅读 Using Promises on MDN


至于如何合成,因为Task是一个Monad你可以用map, ap, chain, apSecond等等

例如,假设 addAtEnd 定义如下:

const addAtEnd = (b: string) => (a: string): string => a + b

您可以通过 Task.map:

将其与 getHello() 一起使用
import * as T from 'fp-ts/Task'
import { pipe } from 'fp-ts/function'

// type of map:
// export declare const map: <A, B>(f: (a: A) => B) => (fa: Task<A>) => Task<B>

// Task<string> which, when called, would resolve to 'hello world'
const getHelloAndAddWorld = pipe(
  getHello,
  T.map(addAtEnd(' world'))
)

// same as
const getHelloAndAddWorld = T.map(addAtEnd(' world'))(getHello)

或者如果你想记录它的值,你可以使用 chainIOK and Console.log:

import * as Console from 'fp-ts/Console'

// type of T.chainIOK:
// export declare function chainIOK<A, B>(f: (a: A) => IO<B>): (ma: Task<A>) => Task<B>

// type of Console.log:
// export declare function log(s: unknown): IO<void>

// Note that IO<A> is a function that (usually) does a side-effect and returns A
// (() => A)

// Task<void>
const logHelloAndWorld = pipe(
  getHelloAndAddWorld,
  T.chainIOK(Console.log)
)

// same as
const logHelloAndWorld = pipe(
  getHello,
  T.map(addAtEnd(' world')),
  T.chainIOK(Console.log)
)

要执行Tasks,只需调用它:

logHelloAndWorld() // logs 'hello world'

对于仿函数、应用程序和单子的简单介绍,Adit's 'Functors, Applicatives, And Monads In Pictures' or Tze-Hsiang Lin's JavaScript version of that 是一些很好的起点。