Open spacejack opened 4 years ago
Also another question, why are the types not inferred here:
fold(l => {
// l is unknown
}, r => {
// r is unknown
})(ID.decode(input))
I think it used to work in V1, if you did T.decode(input).fold(l => {}, r => {})
Hi @spacejack , to answer your second question, with fp-ts v2 you'll have to use pipe
together with fold
to get the correct inference:
import { pipe } from 'fp-ts/lib/pipeable'
import { fold } from 'fp-ts/lib/Either'
pipe(
ID.decode(input),
fold(
l => { /* l is t.Errors */ },
r => { /* r is ID */ }
)
)
Regarding your first question, I would say it depends on your familiarity with other fp concepts, and the willingness to use them in the codebase. For instance, mixing Promises with fp-ts data types is not really advisable in my experience, i.e. you'll keep switching between two different APIs that can't be composed (async/await or Promises in general, versus fp-ts pipes)
Just to give a quick example, If you are willing to switch from Promise<Either>
to TaskEither
inside this codebase, your example above could be rewritten into something like:
import * as E from "fp-ts/lib/Either"
import * as T from "fp-ts/lib/Task"
import * as TE from "fp-ts/lib/TaskEither"
import { pipe } from "fp-ts/lib/pipeable"
// provided some helpers with different signatures...
type User = { name: string }
declare function getUserById(id: ID): TE.TaskEither<HttpStatus, User>
declare function writeErrorResponse(status: HttpStatus): T.Task<void>
declare function writeSuccessResponse(body: User): T.Task<void>
// the async function body could look something similar to:
return pipe(
ID.decode(req.params.id),
E.mapLeft(() => HttpStatus.BAD_REQUEST),
TE.fromEither,
TE.chain(getUserById),
TE.fold(writeErrorResponse, writeSuccessResponse)
)()
Just take this as a possible solution, I'm not saying you are forced to go in this direction to use io-ts
.
Hope this helps :)
@giogonzo great answer! Thanks very much, it clears up a lot. So if I want to keep using async/await, it looks like isLeft
/isRight
make the imperative style fairly workable.
Oh, one other question - what happens to an (unexpected) exception thrown during the piped operations?
I suppose by "unexpected exception" you mean throw new Error()
or Promise.reject()
?
fp-ts
expects to work with pure function implementations - always.
For this reason, throw new Error()
as well as Promise.reject()
is always unexpected and unhandled by the library itself.
If dealing with 3rd parties libraries / APIs where the above is not true, explicit fp-ts APIs are available to resume the computation assuming it's pure and the above assumption holds from there onwards. As described in "Interoperability with non-functional code":
Either.tryCatch
/Option.tryCatch
will catch thrown errors (as in try {} catch {}
)TaskEither.tryCatch
will catch promise rejections
📖 Documentation
Hi, I'm revisiting io-ts after being away for a while. I'm experimenting with it in an express app and I'm not sure of the best code style approach. Here's an example:
I looked at using
pipe
andfold
, but the code seems to get very verbose and very indented. I also needed to use async callbacks... I wasn't sure if errors would be caught properly by Express. Is there a better way to write this?