[英]How to manage monads in FP and specially in fp-ts
I'm very new to functional programming and specially fp-ts
library.我对函数式编程和特别是
fp-ts
库很fp-ts
。
My question includes two parts:我的问题包括两部分:
Task
to IO
or vice versa, how do we manage this, should we stay always on one or should we change as the chain continues?Task
到IO
,反之亦然,我们如何管理这种情况,我们应该始终保持在一个类型上,还是应该随着链的继续而改变? For example let's say we have a couple of functions and we wanna compose them together as below, I know that probably this example is not very practical but it serves the purpose.例如,假设我们有几个函数,我们想将它们组合在一起,如下所示,我知道这个例子可能不太实用,但它可以达到目的。
declare function getRnd(min: number, max: number): IO<number>; // Returns a random number within the range
declare function getPage(pageNo: number): TaskEither<Error, string>; // Make an Http request
declare function getLinks(pageContent: string): Option<string[]>; // Returns some links
// Let's say we wanna get a random page number and then return the links on it
// How do we compose these functions?
const getPageLinks = pipe(
getRnd(2, 4),
IO.chain(getPage), // I'm pretty sure TS will yells at me here
TaskEither.chain(getLinks),
log, // ?
)
1.) turning Monads from one type to another, how do we manage this, should we stay always on one or should we change as the chain continues?
1.) 将 Monad 从一种类型转变为另一种类型,我们如何管理这一点,我们应该始终保持在一种类型上,还是应该随着链的继续而改变?
You want some kind of (natural) transformation to switch from IO
to Task
/ TaskEither
.您想要某种(自然)转换从
IO
切换到Task
/ TaskEither
。 The other way round doesn't make sense to me, as an async effect cannot be converted to a sync one.反过来对我来说没有意义,因为异步效果无法转换为同步效果。
chain
will preserve the structure . chain
将保留结构。 So getPage
in IO.chain(getPage)
needs a signature number -> IO<whatever>
.所以
getPage
IO.chain(getPage)
中的IO.chain(getPage)
需要一个签名number -> IO<whatever>
。 You can instead use map
to add an additional layer of nesting, like:您可以改为使用
map
添加额外的嵌套层,例如:
pipe(getRnd(2, 4), IO.map(getPage)); // I.IO<TE.TaskEither<Error, string>>
In general, there is no right or wrong way, it just depends on the purpose.一般来说,方法没有对错之分,只看目的。 Note, the more nested data types, the more complex it will become to process the inner value(s).
请注意,嵌套的数据类型越多,处理内部值就越复杂。 Part of functional programming with algebraic structures is to avoid unnecessary nesting right at the source.
具有代数结构的函数式编程的一部分是避免在源头进行不必要的嵌套。
In your case it does indeed make sense to consolidate everything in a uniform TaskEither
- you won't have any advantage with a type IO<TaskEither<...>>
vs TaskEither<...>
.在您的情况下,将所有内容合并到一个统一的
TaskEither
中确实TaskEither
- 类型IO<TaskEither<...>>
与TaskEither<...>
相比,您不会有任何优势。
2.) How to simply make typescript follow of these type changes going from one to another?
2.) 如何简单地让打字稿跟随这些从一种到另一种类型的变化?
You can use TaskEither.rightIO
to transform IO
to TaskEither
( CodeSandbox ):您可以使用
TaskEither.rightIO
将IO
转换为TaskEither
( CodeSandbox ):
import { taskEither as TE, io as I, option as O, pipeable as P, either as E } from "fp-ts";
declare function log<T>(t: T): I.IO<T>;
const getPageLinks = P.pipe(
getRnd(2, 4),
TE.rightIO,
TE.chain(getPage),
TE.map(getLinks),
TE.chain(v => TE.rightIO(log(v)))
); // TE.TaskEither<Error, O.Option<string[]>>
This one also works, as TS uses structural typing (but I would recommend former):这个也有效,因为 TS 使用结构类型(但我会推荐前者):
const getPageLinks2 = P.pipe(
getRnd(2, 4),
I.chain(getPage), // this also works
I.map(v => v.then(vv => E.either.map(vv, getLinks))),
I.chain(log)
); // I.IO<Promise<E.Either<Error, O.Option<string[]>>>>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.