github Effect-TS/effect effect@2.3.0

latest releases: @effect/sql-sqlite-bun@0.12.7, @effect/typeclass@0.27.3, @effect/sql-sqlite-react-native@0.14.6...
7 months ago

Minor Changes

  • #2006 96bcee2 Thanks @github-actions! - change Runtime.AsyncFiberException type parameters order from AsyncFiberException<E, A> to AsyncFiberException<A, E = never>

  • #2006 96bcee2 Thanks @github-actions! - change Runtime.Cancel type parameters order from Cancel<E, A> to Cancel<A, E = never>

  • #2006 c77f635 Thanks @github-actions! - change Exit type parameter order from Exit<E, A> to Exit<A, E = never>

  • #2006 e343a74 Thanks @github-actions! - change Resource type parameters order from Resource<E, A> to Resource<A, E = never>

  • #2006 acf1894 Thanks @github-actions! - change FiberMap type parameters order from FiberMap<K, E = unknown, A = unknown> to FiberMap<K, A = unknown, E = unknown>

  • #2006 9a2d1c1 Thanks @github-actions! - With this change we now require a string key to be provided for all tags and renames the dear old Tag to GenericTag, so when previously you could do:

    import { Effect, Context } from "effect";
    interface Service {
      readonly _: unique symbol;
    }
    const Service = Context.Tag<
      Service,
      {
        number: Effect.Effect<never, never, number>;
      }
    >();

    you are now mandated to do:

    import { Effect, Context } from "effect";
    interface Service {
      readonly _: unique symbol;
    }
    const Service = Context.GenericTag<
      Service,
      {
        number: Effect.Effect<never, never, number>;
      }
    >("Service");

    This makes by default all tags globals and ensures better debuggaility when unexpected errors arise.

    Furthermore we introduce a new way of constructing tags that should be considered the new default:

    import { Effect, Context } from "effect";
    class Service extends Context.Tag("Service")<
      Service,
      {
        number: Effect.Effect<never, never, number>;
      }
    >() {}
    
    const program = Effect.flatMap(Service, ({ number }) => number).pipe(
      Effect.flatMap((_) => Effect.log(`number: ${_}`)),
    );

    this will use "Service" as the key and will create automatically an opaque identifier (the class) to be used at the type level, it does something similar to the above in a single shot.

  • #2006 1a77f72 Thanks @github-actions! - change Effect type parameters order from Effect<R, E, A> to Effect<A, E = never, R = never>

  • #2006 c986f0e Thanks @github-actions! - change FiberSet type parameters order from FiberSet<E, A> to FiberSet<A, E = never>

  • #2006 96bcee2 Thanks @github-actions! - change Runtime.RunCallbackOptions type parameters order from RunCallbackOptions<E, A> to RunCallbackOptions<A, E = never>

  • #2006 70dde23 Thanks @github-actions! - change TDeferred type parameters order from TDeferred<E, A> to TDeferred<A, E = never>

  • #2006 81b7425 Thanks @github-actions! - change Streamable.Class and Effectable.Class type parameters order from Class<R, E, A> to Class<A, E = never, R = never>

  • #2006 02c3461 Thanks @github-actions! - With this change we remove the Data.Data type and we make Equal.Equal & Hash.Hash implicit traits.

    The main reason is that Data.Data<A> was structurally equivalent to A & Equal.Equal but extending Equal.Equal doesn't mean that the equality is implemented by-value, so the type was simply adding noise without gaining any level of safety.

    The module Data remains unchanged at the value level, all the functions previously available are supposed to work in exactly the same manner.

    At the type level instead the functions return Readonly variants, so for example we have:

    import { Data } from "effect";
    
    const obj = Data.struct({
      a: 0,
      b: 1,
    });

    will have the obj typed as:

    declare const obj: {
      readonly a: number;
      readonly b: number;
    };
  • #2006 0e56e99 Thanks @github-actions! - change Deferred type parameters order from Deferred<E, A> to Deferred<A, E>

  • #2006 8b0ded9 Thanks @github-actions! - change Fiber type parameters order from Fiber<E, A> to Fiber<A, E = never>

  • #2006 8dd83e8 Thanks @github-actions! - change Channel type parameters order from Channel<out Env, in InErr, in InElem, in InDone, out OutErr, out OutElem, out OutDone> to Channel<OutElem, InElem = unknown, OutErr = never, InErr = unknown, OutDone = void, InDone = unknown, Env = never>

  • #2006 d75f6fe Thanks @github-actions! - change Take type parameters order from Take<E, A> to Take<A, E = never>

  • #2006 7356e5c Thanks @github-actions! - change STM type parameters order from STM<R, E, A> to STM<A, E = never, R = never>

  • #2006 3077cde Thanks @github-actions! - change Stream type parameters order from Stream<R, E, A> to Stream<A, E = never, R = never>

  • #2006 78f47ab Thanks @github-actions! - change Pool type parameters order from Pool<E, A> to Pool<A, E = never>, and KeyedPool from KeyedPool<E, A> to KeyedPool<A, E = never>

  • #2006 52e5d20 Thanks @github-actions! - change Request type parameters order from Request<E, A> to Request<A, E = never>

  • #2006 c6137ec Thanks @github-actions! - change RuntimeFiber type parameters order from RuntimeFiber<E, A> to RuntimeFiber<A, E = never>

  • #2006 f5ae081 Thanks @github-actions! - Use TimeoutException instead of NoSuchElementException for timeout.

  • #2006 60686f5 Thanks @github-actions! - change Layer type parameters order from Layer<RIn, E, ROut> to Layer<ROut, E = never, RIn = never>

  • #2006 9a2d1c1 Thanks @github-actions! - This change enables Effect.serviceConstants and Effect.serviceMembers to access any constant in the service, not only the effects, namely it is now possible to do:

    import { Effect, Context } from "effect";
    
    class NumberRepo extends Context.TagClass("NumberRepo")<
      NumberRepo,
      {
        readonly numbers: Array<number>;
      }
    >() {
      static numbers = Effect.serviceConstants(NumberRepo).numbers;
    }
  • #2006 5127afe Thanks @github-actions! - Rename ReadonlyRecord.update to .replace

  • #2006 8ee2931 Thanks @github-actions! - enhance DX by swapping type parameters and adding defaults to:

    • Effect
      • async
      • asyncOption
      • asyncEither
    • Stream
      • asyncEffect
      • asyncInterrupt
      • asyncOption
      • asyncScoped
      • identity
  • #2006 6727474 Thanks @github-actions! - change Sink type parameters order from Sink<out R, out E, in In, out L, out Z> to Sink<out A, in In = unknown, out L = never, out E = never, out R = never>

  • #2006 5127afe Thanks @github-actions! - rename ReadonlyRecord.upsert to .set

Patch Changes

  • #2006 5127afe Thanks @github-actions! - add ReadonlyRecord.modify

  • #2083 be19ce0 Thanks @mikearnaldi! - Add Ratelimiter which limits the number of calls to a resource within a time window using the token bucket algorithm.

    Usage Example:

    import { Effect, RateLimiter } from "effect";
    
    // we need a scope because the rate limiter needs to allocate a state and a background job
    const program = Effect.scoped(
      Effect.gen(function* ($) {
        // create a rate limiter that executes up to 10 requests within 2 seconds
        const rateLimit = yield* $(RateLimiter.make(10, "2 seconds"));
        // simulate repeated calls
        for (let n = 0; n < 100; n++) {
          // wrap the effect we want to limit with rateLimit
          yield* $(rateLimit(Effect.log("Calling RateLimited Effect")));
        }
      }),
    );
    
    // will print 10 calls immediately and then throttle
    program.pipe(Effect.runFork);

    Or, in a more real world scenario, with a dedicated Service + Layer:

    import { Context, Effect, Layer, RateLimiter } from "effect";
    
    class ApiLimiter extends Context.Tag("@services/ApiLimiter")<
      ApiLimiter,
      RateLimiter.RateLimiter
    >() {
      static Live = RateLimiter.make(10, "2 seconds").pipe(
        Layer.scoped(ApiLimiter),
      );
    }
    
    const program = Effect.gen(function* ($) {
      const rateLimit = yield* $(ApiLimiter);
      for (let n = 0; n < 100; n++) {
        yield* $(rateLimit(Effect.log("Calling RateLimited Effect")));
      }
    });
    
    program.pipe(Effect.provide(ApiLimiter.Live), Effect.runFork);
  • #2084 4a5d01a Thanks @tim-smart! - simplify RateLimiter implementation using semaphore

  • #2084 4a5d01a Thanks @tim-smart! - add Number.nextPow2

    This function returns the next power of 2 from the given number.

    import { nextPow2 } from "effect/Number";
    
    assert.deepStrictEqual(nextPow2(5), 8);
    assert.deepStrictEqual(nextPow2(17), 32);

Don't miss a new effect release

NewReleases is sending notifications on new releases.