This project is part of my weekly newsletter at sandromaglione.com.
This is a collection of examples of how to use effect
.
This is a deep dive exploration of the features and internals offered by effect
.
Install:
pnpm install
Then you can run each script (take a look at package.json
for the list of all the scripts you can run):
pnpm run index
Note: The convention is that files that start with a lowercase letter are runnable scripts
Take a look also at test
.
No need to install extra libraries like js-base64
. The Encoding
module provides encoding/decoding of base64.
Inside GlobalValue
Effect stores a globalStore
of type Map<unknown, any>
.
Inside this store the Context
module registers a tagRegistry
:
const tagRegistry = globalValue("effect/Context/Tag/tagRegistry", () => new Map<any, C.Tag<any, any>>())
Inside this registry Effect collects all Tag
s, created using Context.Tag
by calling the internal makeTag
.
Note
When you pass an identifier
to Context.Tag
you specify a key for the tagRegistry
. Effect checks the registry when accessing and creating a new tag.
A Tag
is an instance of Pipeable
. This allows to chain .pipe
to an instance of Tag
to extract the methods of a service:
interface Base64Service {
readonly decode: (
source: string
) => Effect.Effect<never, Base64DecodeError, string>;
}
export const Base64Service = Context.Tag<Base64Service>("@app/Base64Service");
/** Use `pipe` to extract the methods from the service 👆 */
const result = Base64.Base64Service.pipe(
Effect.flatMap((base64) => base64.decode("Zm9vYmFy")),
Effect.provide(Base64.Base64ServiceLive), // 👈 Then provide a valid instance
Effect.runSync
);
Tag
(Context
) is also a valid instance of Effect
. This allows to use methods such as flatMap
.
An instance of Context
is created by copying a context prototype (using Object.create
inside the internal makeContext
)
The Iteration protocols define an Iterable
interface:
interface Iterator<T, TReturn = any, TNext = undefined> {
next(...args: [] | [TNext]): IteratorResult<T, TReturn>;
return?(value?: TReturn): IteratorResult<T, TReturn>;
throw?(e?: any): IteratorResult<T, TReturn>;
}
interface Iterable<T> {
[Symbol.iterator](): Iterator<T>;
}
The Effect
type defines an iterator. This allows to use yield*
in Effect.gen
(EffectGen
and SingleShotGen
).
This is also used by Effect to define methods such as map
.