Skip to content

Commit

Permalink
Make Effect.Tag a Live Layer
Browse files Browse the repository at this point in the history
  • Loading branch information
mikearnaldi committed Sep 26, 2024
1 parent a895f76 commit e684cef
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 43 deletions.
62 changes: 46 additions & 16 deletions packages/effect/src/Effect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6194,15 +6194,16 @@ export declare namespace Tag {
key?: `property "key" is forbidden`
stack?: `property "stack" is forbidden`
name?: `property "name" is forbidden`
pipe?: `property "pipe" is forbidden`
}

/**
* @since 2.0.0
* @category models
*/
export interface ProhibitedTypeLive extends ProhibitedType {
Live?: `property "Live" is forbidden`
Layer?: `property "Layer" is forbidden`
_op_layer?: `property "_op_layer" is forbidden`
}

/**
Expand Down Expand Up @@ -6245,8 +6246,14 @@ export declare namespace Tag {
*/
export type Maker = {
effect: Effect<AllowedType<ProhibitedTypeLive>, any, any>
} | {
effect: Effect<AllowedType<ProhibitedTypeLive>, any, any>
dependencies: [Layer.Layer<any, any, any>, ...Array<Layer.Layer<any, any, any>>]
} | {
scoped: Effect<AllowedType<ProhibitedTypeLive>, any, any>
} | {
scoped: Effect<AllowedType<ProhibitedTypeLive>, any, any>
dependencies: [Layer.Layer<any, any, any>, ...Array<Layer.Layer<any, any, any>>]
} | {
sync: () => AllowedType<ProhibitedTypeLive>
}
Expand All @@ -6257,19 +6264,41 @@ export declare namespace Tag {
*/
export type ReturnWithMaker<Self, Id extends string, Maker extends Tag.Maker> =
& {}
& Maker extends { scoped: Effect<infer Type extends AllowedType<ProhibitedTypeLive>, infer E, infer R> }
? Tag.Return<Self, Id, Type> & {
readonly Layer: Layer.Layer<Self, E, Exclude<R, Scope.Scope>>
readonly Live: Layer.Layer<Self, E, Exclude<R, Scope.Scope>>
}
& Maker extends {
scoped: Effect<infer Type extends AllowedType<ProhibitedTypeLive>, infer E, infer R>
dependencies: infer Layers extends [Layer.Layer<any, any, any>, ...Array<Layer.Layer<any, any, any>>]
} ?
& Tag.Return<Self, Id, Type>
& Layer.Layer<
Self,
E | { [k in keyof Layers]: Layer.Layer.Error<Layers[k]> }[number],
| Exclude<R, Scope.Scope | { [k in keyof Layers]: Layer.Layer.Success<Layers[k]> }[number]>
| { [k in keyof Layers]: Layer.Layer.Context<Layers[k]> }[number]
>
& { readonly Layer: Layer.Layer<Self, E, Exclude<R, Scope.Scope>> }
: Maker extends { scoped: Effect<infer Type extends AllowedType<ProhibitedTypeLive>, infer E, infer R> }
? Tag.Return<Self, Id, Type> & Layer.Layer<Self, E, Exclude<R, Scope.Scope>> & {
readonly Layer: Layer.Layer<Self, E, Exclude<R, Scope.Scope>>
}
: Maker extends {
effect: Effect<infer Type extends AllowedType<ProhibitedTypeLive>, infer E, infer R>
dependencies: infer Layers extends [Layer.Layer<any, any, any>, ...Array<Layer.Layer<any, any, any>>]
} ?
& Tag.Return<Self, Id, Type>
& Layer.Layer<
Self,
E | { [k in keyof Layers]: Layer.Layer.Error<Layers[k]> }[number],
| Exclude<R, { [k in keyof Layers]: Layer.Layer.Success<Layers[k]> }[number]>
| { [k in keyof Layers]: Layer.Layer.Context<Layers[k]> }[number]
>
& { readonly Layer: Layer.Layer<Self, E, Exclude<R, Scope.Scope>> }
: Maker extends { effect: Effect<infer Type extends AllowedType<ProhibitedTypeLive>, infer E, infer R> }
? Tag.Return<Self, Id, Type> & {
? Tag.Return<Self, Id, Type> & Layer.Layer<Self, E, R> & {
readonly Layer: Layer.Layer<Self, E, R>
readonly Live: Layer.Layer<Self, E, R>
}
: Maker extends { sync: () => infer Type extends AllowedType<ProhibitedTypeLive> } ? Tag.Return<Self, Id, Type> & {
: Maker extends { sync: () => infer Type extends AllowedType<ProhibitedTypeLive> } ?
Tag.Return<Self, Id, Type> & Layer.Layer<Self, never, never> & {
readonly Layer: Layer.Layer<Self, never, never>
readonly Live: Layer.Layer<Self, never, never>
}
: never
}
Expand Down Expand Up @@ -6301,22 +6330,23 @@ export const Tag: {
})
if (maker !== undefined) {
if ("effect" in maker) {
// @ts-expect-error
TagClass["Live"] = layer.fromEffect(TagClass, maker["effect"])
// @ts-expect-error
TagClass["Layer"] = layer.fromEffect(TagClass, maker["effect"])
Object.assign(TagClass, TagClass["Layer"])
}
if ("scoped" in maker) {
// @ts-expect-error
TagClass["Live"] = layer.scoped(TagClass, maker["scoped"])
// @ts-expect-error
TagClass["Layer"] = layer.scoped(TagClass, maker["scoped"])
let live = TagClass["Layer"]
if ("dependencies" in maker) {
live = live.pipe(layer.provide(layer.mergeAll(...maker["dependencies"])))
}
Object.assign(TagClass, live)
}
if ("sync" in maker) {
// @ts-expect-error
TagClass["Live"] = layer.sync(TagClass, maker["sync"])
// @ts-expect-error
TagClass["Layer"] = layer.sync(TagClass, maker["sync"])
Object.assign(TagClass, TagClass["Layer"])
}
}
const cache = new Map()
Expand Down
28 changes: 14 additions & 14 deletions packages/effect/src/internal/layer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export type Primitive =

/** @internal */
export type Op<Tag extends string, Body = {}> = Layer.Layer<unknown, unknown, unknown> & Body & {
readonly _tag: Tag
readonly _op_layer: Tag
}

/** @internal */
Expand Down Expand Up @@ -165,7 +165,7 @@ export const isLayer = (u: unknown): u is Layer.Layer<unknown, unknown, unknown>

/** @internal */
export const isFresh = <RIn, E, ROut>(self: Layer.Layer<ROut, E, RIn>): boolean => {
return (self as Primitive)._tag === OpCodes.OP_FRESH
return (self as Primitive)._op_layer === OpCodes.OP_FRESH
}

// -----------------------------------------------------------------------------
Expand Down Expand Up @@ -356,7 +356,7 @@ const makeBuilder = <RIn, E, ROut>(
inMemoMap = false
): Effect.Effect<(memoMap: Layer.MemoMap) => Effect.Effect<Context.Context<ROut>, E, RIn>> => {
const op = self as Primitive
switch (op._tag) {
switch (op._op_layer) {
case "Locally": {
return core.sync(() => (memoMap: Layer.MemoMap) => op.f(memoMap.getOrElseMemoize(op.self, scope)))
}
Expand Down Expand Up @@ -487,7 +487,7 @@ export const extendScope = <RIn, E, ROut>(
self: Layer.Layer<ROut, E, RIn>
): Layer.Layer<ROut, E, RIn | Scope.Scope> => {
const extendScope = Object.create(proto)
extendScope._tag = OpCodes.OP_EXTEND_SCOPE
extendScope._op_layer = OpCodes.OP_EXTEND_SCOPE
extendScope.layer = self
return extendScope
}
Expand Down Expand Up @@ -533,7 +533,7 @@ export const flatten = dual<
/** @internal */
export const fresh = <A, E, R>(self: Layer.Layer<A, E, R>): Layer.Layer<A, E, R> => {
const fresh = Object.create(proto)
fresh._tag = OpCodes.OP_FRESH
fresh._op_layer = OpCodes.OP_FRESH
fresh.layer = self
return fresh
}
Expand Down Expand Up @@ -565,7 +565,7 @@ export function fromEffectContext<A, E, R>(
effect: Effect.Effect<Context.Context<A>, E, R>
): Layer.Layer<A, E, R> {
const fromEffect = Object.create(proto)
fromEffect._tag = OpCodes.OP_FROM_EFFECT
fromEffect._op_layer = OpCodes.OP_FROM_EFFECT
fromEffect.effect = effect
return fromEffect
}
Expand All @@ -587,7 +587,7 @@ export const locallyEffect = dual<
) => Layer.Layer<ROut2, E2, RIn2>
>(2, (self, f) => {
const locally = Object.create(proto)
locally._tag = "Locally"
locally._op_layer = "Locally"
locally.self = self
locally.f = f
return locally
Expand Down Expand Up @@ -658,7 +658,7 @@ export const matchCause = dual<
) => Layer.Layer<A2 & A3, E2 | E3, R | R2 | R3>
>(2, (self, { onFailure, onSuccess }) => {
const fold = Object.create(proto)
fold._tag = OpCodes.OP_FOLD
fold._op_layer = OpCodes.OP_FOLD
fold.layer = self
fold.failureK = onFailure
fold.successK = onSuccess
Expand Down Expand Up @@ -866,7 +866,7 @@ export const scopedContext = <A, E, R>(
effect: Effect.Effect<Context.Context<A>, E, R>
): Layer.Layer<A, E, Exclude<R, Scope.Scope>> => {
const scoped = Object.create(proto)
scoped._tag = OpCodes.OP_SCOPED
scoped._op_layer = OpCodes.OP_SCOPED
scoped.effect = effect
return scoped
}
Expand Down Expand Up @@ -920,7 +920,7 @@ export const suspend = <RIn, E, ROut>(
evaluate: LazyArg<Layer.Layer<ROut, E, RIn>>
): Layer.Layer<ROut, E, RIn> => {
const suspend = Object.create(proto)
suspend._tag = OpCodes.OP_SUSPEND
suspend._op_layer = OpCodes.OP_SUSPEND
suspend.evaluate = evaluate
return suspend
}
Expand Down Expand Up @@ -1039,9 +1039,9 @@ export const provide = dual<
) =>
suspend(() => {
const provideTo = Object.create(proto)
provideTo._tag = OpCodes.OP_PROVIDE
provideTo._op_layer = OpCodes.OP_PROVIDE
provideTo.first = Object.create(proto, {
_tag: { value: OpCodes.OP_PROVIDE_MERGE, enumerable: true },
_op_layer: { value: OpCodes.OP_PROVIDE_MERGE, enumerable: true },
first: { value: context<Exclude<RIn2, ROut>>(), enumerable: true },
second: { value: self },
zipK: { value: (a: Context.Context<ROut>, b: Context.Context<ROut2>) => pipe(a, Context.merge(b)) }
Expand All @@ -1063,7 +1063,7 @@ export const provideMerge = dual<
) => Layer.Layer<ROut | ROut2, E2 | E, RIn | Exclude<RIn2, ROut>>
>(2, <RIn2, E2, ROut2, RIn, E, ROut>(that: Layer.Layer<ROut2, E2, RIn2>, self: Layer.Layer<ROut, E, RIn>) => {
const zipWith = Object.create(proto)
zipWith._tag = OpCodes.OP_PROVIDE_MERGE
zipWith._op_layer = OpCodes.OP_PROVIDE_MERGE
zipWith.first = self
zipWith.second = provide(that, self)
zipWith.zipK = (a: Context.Context<ROut>, b: Context.Context<ROut2>): Context.Context<ROut | ROut2> => {
Expand All @@ -1086,7 +1086,7 @@ export const zipWith = dual<
>(3, (self, that, f) =>
suspend(() => {
const zipWith = Object.create(proto)
zipWith._tag = OpCodes.OP_ZIP_WITH
zipWith._op_layer = OpCodes.OP_ZIP_WITH
zipWith.first = self
zipWith.second = that
zipWith.zipK = f
Expand Down
24 changes: 11 additions & 13 deletions packages/effect/test/Effect/environment.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,9 @@ class DemoTag extends Effect.Tag("DemoTag")<DemoTag, {
}>() {
}

class DateTag extends Effect.Tag("DateTag")<DateTag, Date>() {
static date = new Date(1970, 1, 1)
static Live = Layer.succeed(this, this.date)
}
class DateTag extends Effect.Tag("DateTag", {
sync: () => new Date(1970, 1, 1)
})<DateTag>() {}

class MapTag extends Effect.Tag("MapTag", {
effect: Effect.sync(() => new Map<string, string>())
Expand All @@ -53,10 +52,9 @@ class ScopedTag extends Effect.Tag("ScopedTag", {
yield* Effect.acquireRelease(Effect.sync(() => 100), () => Effect.void)
yield* Dummy
return 100
})
})<ScopedTag>() {
static Live = this.Layer.pipe(Layer.provide(Dummy.Live))
}
}),
dependencies: [Dummy]
})<ScopedTag>() {}

describe("Effect", () => {
it.effect("provide runtime is additive", () =>
Expand Down Expand Up @@ -108,7 +106,7 @@ describe("Effect", () => {
})))
it.effect("effect tag with primitives", () =>
Effect.gen(function*($) {
expect(yield* $(DateTag.getTime())).toEqual(DateTag.date.getTime())
expect(yield* $(DateTag.getTime())).toEqual(new Date(1970, 1, 1).getTime())
expect(yield* $(NumberTag)).toEqual(100)
expect(yield* $(ScopedTag)).toEqual(100)
expect(Array.from(yield* $(MapTag.keys()))).toEqual([])
Expand All @@ -117,10 +115,10 @@ describe("Effect", () => {
expect(yield* $(MapTag.get("foo"))).toEqual("bar")
}).pipe(
Effect.provide(Layer.mergeAll(
DateTag.Live,
NumberTag.Live,
MapTag.Live,
ScopedTag.Live
DateTag,
NumberTag,
MapTag,
ScopedTag
))
))
it.effect("class tag", () =>
Expand Down

0 comments on commit e684cef

Please sign in to comment.