Effect 3.9 (Release)

Oct 7th, 2024

Effect 3.9 has been released! This release includes a number of new features and improvements. Here's a summary of what's new:

Effect.Service

To make the creation of services in Effect easier, the Effect.Service api has been introduced.

It allows you to define both a Context.Tag and a Layer for a service in one pass, optionally giving you the ability to provide any dependencies at the same time.

typescript
import { FileSystem } from "@effect/platform"
import { NodeFileSystem } from "@effect/platform-node"
import type { Layer } from "effect"
import * as Effect from "effect/Effect"
export class Cache extends Effect.Service<Cache>()("app/Cache", {
// define how to create the service
// You can also use the "scoped", "sync" or "succeed" keys to create your service
effect: Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem
const lookup = (key: string) => fs.readFileString(`cache/${key}`)
return { lookup } as const
}),
// provide dependencies
dependencies: [NodeFileSystem.layer]
}) {}
// Layer for use in the application
const layer: Layer.Layer<Cache> = Cache.Default
// Layer without dependencies provided
const layerNoDeps: Layer.Layer<Cache, never, FileSystem.FileSystem> =
Cache.DefaultWithoutDependencies
// `Cache` type also represents the service itself
declare const cache: Cache
cache.lookup("foo")
typescript
import { FileSystem } from "@effect/platform"
import { NodeFileSystem } from "@effect/platform-node"
import type { Layer } from "effect"
import * as Effect from "effect/Effect"
export class Cache extends Effect.Service<Cache>()("app/Cache", {
// define how to create the service
// You can also use the "scoped", "sync" or "succeed" keys to create your service
effect: Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem
const lookup = (key: string) => fs.readFileString(`cache/${key}`)
return { lookup } as const
}),
// provide dependencies
dependencies: [NodeFileSystem.layer]
}) {}
// Layer for use in the application
const layer: Layer.Layer<Cache> = Cache.Default
// Layer without dependencies provided
const layerNoDeps: Layer.Layer<Cache, never, FileSystem.FileSystem> =
Cache.DefaultWithoutDependencies
// `Cache` type also represents the service itself
declare const cache: Cache
cache.lookup("foo")

Effect/Layer.provide accepts multiple layers

The Effect.provide & Layer.provide apis can now accept multiple layers to be provided.

typescript
someEffect.pipe(Effect.provide([layer1, layer2, layer3]))
typescript
someEffect.pipe(Effect.provide([layer1, layer2, layer3]))

Effect.provide now supports ManagedRuntime

You can now provide a ManagedRuntime to an effect, allowing you to use the services from the ManagedRuntime inside of the effect.

typescript
import { Effect, Layer, ManagedRuntime } from "effect"
const runtime = ManagedRuntime.make(Layer.empty)
someEffect.pipe(Effect.provide(runtime))
typescript
import { Effect, Layer, ManagedRuntime } from "effect"
const runtime = ManagedRuntime.make(Layer.empty)
someEffect.pipe(Effect.provide(runtime))

Effect.mapAccum & Array.mapAccum preserve non-emptyness

If you use a NonEmptyArray with Effect.mapAccum or Array.mapAccum, the result will now be typed as a NonEmptyArray.

Predicate.isRegExp

You can use this api to determine if a value is a RegExp.

typescript
import { Predicate } from "effect"
assert.deepStrictEqual(Predicate.isRegExp(/a/), true)
assert.deepStrictEqual(Predicate.isRegExp("a"), false)
typescript
import { Predicate } from "effect"
assert.deepStrictEqual(Predicate.isRegExp(/a/), true)
assert.deepStrictEqual(Predicate.isRegExp("a"), false)

Tuple.map

Tuple.map can be used to transform each element of a tuple using a function.

typescript
import { pipe, Tuple } from "effect"
const result = pipe(
// ^? [string, string, string]
["a", 1, false] as const,
Tuple.map((el) => {
// ^? "a" | 1 | false
return el.toString().toUpperCase()
})
)
assert.deepStrictEqual(result, ["A", "1", "FALSE"])
typescript
import { pipe, Tuple } from "effect"
const result = pipe(
// ^? [string, string, string]
["a", 1, false] as const,
Tuple.map((el) => {
// ^? "a" | 1 | false
return el.toString().toUpperCase()
})
)
assert.deepStrictEqual(result, ["A", "1", "FALSE"])

Array.pad

This api can be used to add elements to the end of an array until it reaches the desired length.

typescript
import { Array } from "effect"
const arr = [1, 2, 3]
const result = Array.pad(arr, 6, 0)
assert.deepStrictEqual(result, [1, 2, 3, 0, 0, 0])
typescript
import { Array } from "effect"
const arr = [1, 2, 3]
const result = Array.pad(arr, 6, 0)
assert.deepStrictEqual(result, [1, 2, 3, 0, 0, 0])

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!