#typescript #functional-programming #fp-ts
#typescript #функциональное программирование #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')
)
Комментарии:
1.
async
функции всегда возвращают обещания, это не имеет ничего общего с fp-ts.Task
похоже, это просто псевдоним для чего-то вызываемого, который возвращает обещание, которымasync
также будет функция.
Ответ №1:
Сначала давайте разберемся, что Task
есть на самом деле.
export interface Task<A> {
(): Promise<A>
}
// note that this could also be written as
export type Task<A> = () => Promise<A>
A Task
— это просто функция, которая возвращает a Promise
, поэтому в вашем примере вызов getHello
вернет a 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())
Для получения дополнительной информации об обещаниях я рекомендую прочитать Использование обещаний в MDN.
Что касается того, как с ним работать, поскольку Task
Monad
вы можете использовать map
, ap
, chain
, apSecond
и т. Д.
Например, допустим addAtEnd
, был определен следующим образом:
const addAtEnd = (b: string) => (a: string): string => a b
Вы можете использовать это с getHello()
помощью Task.map
:
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
и 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)
)
Чтобы выполнить Task
s, просто вызовите его:
logHelloAndWorld() // logs 'hello world'
Для простого ознакомления с функторами, аппликативами и монадами, хорошей отправной точкой являются «Функторы, аппликативы и монады в картинках» Adit или их версия для JavaScript от Tze-Hsiang Lin.
Комментарии:
1. большое спасибо, что нашли время объяснить мне это, это очень много значит! теперь я понимаю это лучше.
Task
map
принимает чистую программу и применяется кTask
ней еще до того, как она будет разрешена с помощью .then
я знаю, что это другой вопрос, и мне жаль, но я попытался сделать то же самое, что и вы, и это выдает мне ошибку. кроме того, знаете ли вы, есть ли какой-либо способ извлечь значение из задачи? если бы вы могли разобраться в этом: codesandbox.io/s/young-field-lnzi2?file=/src/index.ts2. или, другими словами, как мне выполнить его после его составления?
3. @padowbr Извините, я забыл добавить такую простую вещь! Чтобы выполнить a
Task
, просто вызовите его:logHelloAndWorld()
. То же самое и дляIO
s.4. И извините за ошибку, моя ошибка. Я исправил это в своем ответе; это должно быть
getHello
, а неgetHello()
.5. все в порядке! большое вам спасибо. но что, если вместо того, чтобы записывать разрешенное значение в консоль, я хочу вернуть само значение?