Skip to content

Commit 6d9393a

Browse files
authored
add ServiceMap.mutate (#1729)
1 parent 1521d02 commit 6d9393a

File tree

2 files changed

+66
-31
lines changed

2 files changed

+66
-31
lines changed

.changeset/lazy-queens-rush.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"effect": patch
3+
---
4+
5+
add ServiceMap.mutate

packages/effect/src/ServiceMap.ts

Lines changed: 61 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,7 @@ export interface ServiceMap<in Services> extends Equal.Equal, Pipeable, Inspecta
371371
readonly _Services: Types.Contravariant<Services>
372372
}
373373
readonly mapUnsafe: ReadonlyMap<string, any>
374+
mutable: boolean
374375
}
375376

376377
/**
@@ -392,10 +393,11 @@ export interface ServiceMap<in Services> extends Equal.Equal, Pipeable, Inspecta
392393
export const makeUnsafe = <Services = never>(mapUnsafe: ReadonlyMap<string, any>): ServiceMap<Services> => {
393394
const self = Object.create(Proto)
394395
self.mapUnsafe = mapUnsafe
396+
self.mutable = false
395397
return self
396398
}
397399

398-
const Proto: Omit<ServiceMap<never>, "mapUnsafe"> = {
400+
const Proto: Omit<ServiceMap<never>, "mapUnsafe" | "mutable"> = {
399401
...PipeInspectableProto,
400402
[TypeId]: {
401403
_Services: (_: never) => _
@@ -558,11 +560,10 @@ export const add: {
558560
self: ServiceMap<Services>,
559561
key: Key<I, S>,
560562
service: Types.NoInfer<S>
561-
): ServiceMap<Services | I> => {
562-
const map = new Map(self.mapUnsafe)
563-
map.set(key.key, service)
564-
return makeUnsafe(map)
565-
})
563+
): ServiceMap<Services | I> =>
564+
withMapUnsafe(self, (map) => {
565+
map.set(key.key, service)
566+
}))
566567

567568
/**
568569
* @since 4.0.0
@@ -582,15 +583,14 @@ export const addOrOmit: {
582583
self: ServiceMap<Services>,
583584
key: Key<I, S>,
584585
service: Option.Option<Types.NoInfer<S>>
585-
): ServiceMap<Services | I> => {
586-
const map = new Map(self.mapUnsafe)
587-
if (service._tag === "None") {
588-
map.delete(key.key)
589-
} else {
590-
map.set(key.key, service.value)
591-
}
592-
return makeUnsafe(map)
593-
})
586+
): ServiceMap<Services | I> =>
587+
withMapUnsafe(self, (map) => {
588+
if (service._tag === "None") {
589+
map.delete(key.key)
590+
} else {
591+
map.set(key.key, service.value)
592+
}
593+
}))
594594

595595
/**
596596
* Get a service from the context that corresponds to the given key, or
@@ -844,9 +844,9 @@ export const merge: {
844844
} = dual(2, <Services, R1>(self: ServiceMap<Services>, that: ServiceMap<R1>): ServiceMap<Services | R1> => {
845845
if (self.mapUnsafe.size === 0) return that as any
846846
if (that.mapUnsafe.size === 0) return self as any
847-
const map = new Map(self.mapUnsafe)
848-
that.mapUnsafe.forEach((value, key) => map.set(key, value))
849-
return makeUnsafe(map)
847+
return withMapUnsafe(self, (map) => {
848+
that.mapUnsafe.forEach((value, key) => map.set(key, value))
849+
})
850850
})
851851

852852
/**
@@ -924,16 +924,14 @@ export const mergeAll = <T extends Array<unknown>>(
924924
export const pick = <S extends ReadonlyArray<Key<any, any>>>(
925925
...services: S
926926
) =>
927-
<Services>(self: ServiceMap<Services>): ServiceMap<Services & Service.Identifier<S[number]>> => {
928-
const map = new Map<string, any>()
929-
const keySet = new Set(services.map((key) => key.key))
930-
self.mapUnsafe.forEach((value, key) => {
931-
if (keySet.has(key)) {
932-
map.set(key, value)
933-
}
927+
<Services>(self: ServiceMap<Services>): ServiceMap<Services & Service.Identifier<S[number]>> =>
928+
withMapUnsafe(self, (map) => {
929+
const keySet = new Set(services.map((key) => key.key))
930+
map.forEach((_, key) => {
931+
if (keySet.has(key)) return
932+
map.delete(key)
933+
})
934934
})
935-
return makeUnsafe(map)
936-
}
937935

938936
/**
939937
* @example
@@ -964,11 +962,43 @@ export const pick = <S extends ReadonlyArray<Key<any, any>>>(
964962
export const omit = <S extends ReadonlyArray<Key<any, any>>>(
965963
...keys: S
966964
) =>
967-
<Services>(self: ServiceMap<Services>): ServiceMap<Exclude<Services, Service.Identifier<S[number]>>> => {
968-
const map = new Map(self.mapUnsafe)
969-
for (let i = 0; i < keys.length; i++) {
970-
map.delete(keys[i].key)
965+
<Services>(self: ServiceMap<Services>): ServiceMap<Exclude<Services, Service.Identifier<S[number]>>> =>
966+
withMapUnsafe(self, (map) => {
967+
for (let i = 0; i < keys.length; i++) {
968+
map.delete(keys[i].key)
969+
}
970+
})
971+
972+
/**
973+
* Perform a series of mutations on a `ServiceMap`. Prevents unnecessary copying
974+
* of the underlying map when multiple mutations are needed.
975+
*
976+
* @since 4.0.0
977+
* @category Utils
978+
*/
979+
export const mutate: {
980+
<Services, B>(
981+
f: (serviceMap: ServiceMap<Services>) => ServiceMap<B>
982+
): <Services>(self: ServiceMap<Services>) => ServiceMap<B>
983+
<Services, B>(self: ServiceMap<Services>, f: (serviceMap: ServiceMap<Services>) => ServiceMap<B>): ServiceMap<B>
984+
} = dual(
985+
2,
986+
<Services, B>(self: ServiceMap<Services>, f: (serviceMap: ServiceMap<Services>) => ServiceMap<B>): ServiceMap<B> => {
987+
const next = makeUnsafe<Services>(new Map(self.mapUnsafe))
988+
next.mutable = true
989+
const result = f(next)
990+
result.mutable = false
991+
return result
971992
}
993+
)
994+
995+
const withMapUnsafe = <Services, B>(self: ServiceMap<Services>, f: (map: Map<string, any>) => void): ServiceMap<B> => {
996+
if (self.mutable) {
997+
f(self.mapUnsafe as any)
998+
return self as any
999+
}
1000+
const map = new Map(self.mapUnsafe)
1001+
f(map)
9721002
return makeUnsafe(map)
9731003
}
9741004

0 commit comments

Comments
 (0)