Effect 3.5 (Release)
Effect 3.5.0 has been released! This release includes a number of new features and improvements. Here's a summary of what's new:
Better support for Error.cause
If you add a cause
property to Data.Error
or Data.TaggedError
, it will now be properly
forwarded to the cause
property of the Error
instance.
typescript
import { Data } from "effect"class MyError extends Data.Error<{ cause: Error }> {}
typescript
import { Data } from "effect"class MyError extends Data.Error<{ cause: Error }> {}
If you Effect.log
a Cause
containing an error with a cause
property, it will now be
visible in the log output.
@effect/sql-d1
The @effect/sql-d1
package has been released. This package provides @effect/sql
support
for Cloudflare's D1 database.
Added RcRef & RcMap
RcRef
and RcMap
are new reference counted types that can be used to manage resources.
The wrapped resource will be acquired on the first access and released when no longer in use.
RcRef
can be used to manage a single resource, and RcMap
can be used to manage
multiple resources referenced by a key.
typescript
import { Effect, RcMap } from "effect"Effect.gen(function* () {const map = yield* RcMap.make({lookup: (key: string) =>Effect.acquireRelease(Effect.succeed(`acquired ${key}`), () =>Effect.log(`releasing ${key}`),)})// Get "foo" from the map twice, which will only acquire it once// It will then be released once the scope closes.yield* RcMap.get(map, "foo").pipe(Effect.andThen(RcMap.get(map, "foo")),Effect.scoped)})
typescript
import { Effect, RcMap } from "effect"Effect.gen(function* () {const map = yield* RcMap.make({lookup: (key: string) =>Effect.acquireRelease(Effect.succeed(`acquired ${key}`), () =>Effect.log(`releasing ${key}`),)})// Get "foo" from the map twice, which will only acquire it once// It will then be released once the scope closes.yield* RcMap.get(map, "foo").pipe(Effect.andThen(RcMap.get(map, "foo")),Effect.scoped)})
Added Logger.pretty
Logger.pretty
is a new logger that leverages the features of the console
APIs to provide a more visually appealing output.
To try it out, provide it to your program:
typescript
import { Effect, Logger } from "effect"Effect.log("Hello, World!").pipe(Effect.provide(Logger.pretty))
typescript
import { Effect, Logger } from "effect"Effect.log("Hello, World!").pipe(Effect.provide(Logger.pretty))
In Effect 4.0, Logger.pretty
will become default logger.
PubSub replay option
The replay
option adds a replay buffer in front of the given PubSub. The buffer will
replay the last n
messages to any new subscriber.
typescript
Effect.gen(function*() {const messages = [1, 2, 3, 4, 5]const pubsub = yield* PubSub.bounded<number>({ capacity: 16, replay: 3 })yield* PubSub.publishAll(pubsub, messages)const sub = yield* PubSub.subscribe(pubsub)assert.deepStrictEqual(Chunk.toReadonlyArray(yield* Queue.takeAll(sub)), [3, 4, 5])})
typescript
Effect.gen(function*() {const messages = [1, 2, 3, 4, 5]const pubsub = yield* PubSub.bounded<number>({ capacity: 16, replay: 3 })yield* PubSub.publishAll(pubsub, messages)const sub = yield* PubSub.subscribe(pubsub)assert.deepStrictEqual(Chunk.toReadonlyArray(yield* Queue.takeAll(sub)), [3, 4, 5])})
Added Stream.raceAll
Stream.raceAll
races the given streams, with the first stream to emit an item declared the
winner. The resulting stream will emit the items from the winning stream.
typescript
import { Stream, Schedule, Console, Effect } from "effect"const stream = Stream.raceAll(Stream.fromSchedule(Schedule.spaced("1 millis")),Stream.fromSchedule(Schedule.spaced("2 millis")),Stream.fromSchedule(Schedule.spaced("4 millis"))).pipe(Stream.take(6), Stream.tap(Console.log))Effect.runPromise(Stream.runDrain(stream))// Output only from the first stream, the rest streams are interrupted// 0// 1// 2// 3// 4// 5
typescript
import { Stream, Schedule, Console, Effect } from "effect"const stream = Stream.raceAll(Stream.fromSchedule(Schedule.spaced("1 millis")),Stream.fromSchedule(Schedule.spaced("2 millis")),Stream.fromSchedule(Schedule.spaced("4 millis"))).pipe(Stream.take(6), Stream.tap(Console.log))Effect.runPromise(Stream.runDrain(stream))// Output only from the first stream, the rest streams are interrupted// 0// 1// 2// 3// 4// 5
Added Random.make
Random.make
creates a new instance of the Random service from a seed value.
It will calculate the hash of the seed value, and use that to seed the random number generator.
Stream.async buffer options
You can now customize the output buffer options for Stream.async*
:
typescript
import { Stream } from "effect"Stream.async((emit) => {// ...}, { bufferSize: 16, strategy: "dropping" })
typescript
import { Stream } from "effect"Stream.async((emit) => {// ...}, { bufferSize: 16, strategy: "dropping" })
Stream PubSub options
You can now customize the strategy and capacity of the underlying PubSub in the following Stream apis:
Stream.toPubSub
Stream.broadcast*
ts
import { Schedule, Stream } from "effect"// toPubSubStream.fromSchedule(Schedule.spaced(1000)).pipe(Stream.toPubSub({capacity: 16, // or "unbounded"strategy: "dropping", // or "sliding" / "suspend"}),)// also for the broadcast apisStream.fromSchedule(Schedule.spaced(1000)).pipe(Stream.broadcastDynamic({capacity: 16,strategy: "dropping",}),)
ts
import { Schedule, Stream } from "effect"// toPubSubStream.fromSchedule(Schedule.spaced(1000)).pipe(Stream.toPubSub({capacity: 16, // or "unbounded"strategy: "dropping", // or "sliding" / "suspend"}),)// also for the broadcast apisStream.fromSchedule(Schedule.spaced(1000)).pipe(Stream.broadcastDynamic({capacity: 16,strategy: "dropping",}),)
Stream type fixes
Stream
&Channel
run* methods now excludeScope
from theR
type.- Use of
Stream.DynamicTuple
has been replaced withTypes.TupleOf
. - Use
left
/right
naming instead ofself
/that
inStream.mergeRight
&Stream.mergeLeft
.
Other changes
There were several other smaller changes made. Take a look through the CHANGELOG to see them all: CHANGELOG.
Don't forget to join our Discord Community to follow the last updates and discuss every tiny detail!