Got interested in this optimisation in Schema — and decided to find out how it works under the hood:
If you want more perf you can use
ParseResult.flatMap
instead ofEffect.gen
and get eager optimisation skipping the Effect runtime.
— Michael Arnaldi
Given
Schema.transformOrFail
transformers are expected to return Effect<T, ParseResult.ParseIssue, R>
. However, for example, Either
is a subtype of Effect
, so returning Either<T, ParseResult.ParseIssue>
is also valid.
The idea
If a schema only uses Either
-transformations, then its decoders/encoders can be run without involving the runtime.
The optimisation
For convenience, the following type is used in the schema:
type ParseResult<A> = Either<A, ParseResult.ParseIssue>
We need to preserve the transformation type as
Either
and not convert it toEffect
— this is handled by functions inParseResult
under the@category optimisation
, such asParseResult.flatMap
.These helpers are primarily used in the
ParseResult.go
function, which is responsible for walking through all AST nodes of the schema and producing aParseResult.Parser
— the actual encode/decode function.ParseResult.handleForbidden
is the function where the final decision to use the runtime or not is made.
The requirements
From its implementation, it's clear that in order for the optimisation ritual to work, two conditions must be met:
The user's intention — to invoke the
validate/decode/encode
family of functions with theEither
/Option
/Sync
suffix (synchronously)The schema's capability — using
ParseResult<A>
(alias forEither
) specifically in transformations.
Otherwise, the optimisation won’t apply — and the runtime with the synchronous scheduler will be used, which seems effectively the same as Effect.runSync
.
The notes
However, it's also important to note that the schema annotations concurrent
and batching
will not be applied to synchronous transformations — meaning that here, transformations take precedence.
Optimisation through synchronous transformations with ParseResult
potentially improves performance by bypassing the Effect runtime. However, I did not conduct any benchmarks; my interest was solely in how the optimisation is implemented.
I hope this overview will be useful to developers when making design decisions and that it’s helpful to know such an optimisation exists.
Top comments (0)