Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/clean-beds-cheer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@effect/core": patch
---

add Differ datatype
4 changes: 4 additions & 0 deletions packages/core/_src/global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ import { DefaultServices } from "@effect/core/io/DefaultServices/definition"
* @tsplus global
*/
import { Deferred } from "@effect/core/io/Deferred/definition"
/**
* @tsplus global
*/
import { Differ } from "@effect/core/io/Differ/definition"
/**
* @tsplus global
*/
Expand Down
1 change: 1 addition & 0 deletions packages/core/_src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export * as cause from "@effect/core/io/Cause"
export * as clock from "@effect/core/io/Clock"
export * as defaultServices from "@effect/core/io/DefaultServices"
export * as deferred from "@effect/core/io/Deferred"
export * as differ from "@effect/core/io/Differ"
export * as effect from "@effect/core/io/Effect"
export * as executionStrategy from "@effect/core/io/ExecutionStrategy"
export * as exit from "@effect/core/io/Exit"
Expand Down
5 changes: 5 additions & 0 deletions packages/core/_src/io/Differ.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// codegen:start {preset: barrel, include: ./Differ/*.ts, prefix: "@effect/core/io"}
export * from "@effect/core/io/Differ/definition"
export * from "@effect/core/io/Differ/operations"
export * from "@effect/core/io/Differ/OrPatch"
// codegen:end
4 changes: 4 additions & 0 deletions packages/core/_src/io/Differ/ChunkPatch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// codegen:start {preset: barrel, include: ./ChunkPatch/*.ts, prefix: "@effect/core/io/Differ"}
export * from "@effect/core/io/Differ/ChunkPatch/definition"
export * from "@effect/core/io/Differ/ChunkPatch/operations"
// codegen:end
102 changes: 102 additions & 0 deletions packages/core/_src/io/Differ/ChunkPatch/definition.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
export const ChunkPatchSym = Symbol.for("@effect/core/io/Differ.ChunkPatch")
export type ChunkPatchSym = typeof ChunkPatchSym

export const ChunkPatchValueSym = Symbol.for("@effect/core/io/Differ.ChunkPatch.Value")
export type ChunkPatchValueSym = typeof ChunkPatchValueSym

export const ChunkPatchPatchSym = Symbol.for("@effect/core/io/Differ.ChunkPatch.Patch")
export type ChunkPatchPatchSym = typeof ChunkPatchPatchSym

/**
* A patch which describes updates to a chunk of values.
*
* @tsplus type effect/core/io/Differ.ChunkPatch
*/
export interface ChunkPatch<Value, Patch> {
readonly [ChunkPatchSym]: ChunkPatchSym
readonly [ChunkPatchValueSym]: () => Value
readonly [ChunkPatchPatchSym]: () => Patch
}

/**
* @tsplus type effect/core/io/Differ.ChunkPatch.Ops
*/
export interface ChunkPatchOps {
readonly $: ChunkPatchAspects
}
/**
* @tsplus static effect/core/io/Differ.Ops ChunkPatch
*/
export const ChunkPatch: ChunkPatchOps = {
$: {}
}

/**
* @tsplus type effect/core/io/Differ.ChunkPatch.Aspects
*/
export interface ChunkPatchAspects {}

/**
* @tsplus unify effect/core/io/Differ.ChunkPatch
*/
export function unifyChunkPatch<X extends ChunkPatch<any, any>>(self: X): ChunkPatch<
[X] extends [{ [ChunkPatchValueSym]: () => infer Value }] ? Value : never,
[X] extends [{ [ChunkPatchPatchSym]: () => infer Patch }] ? Patch : never
> {
return self
}

export abstract class BaseChunkPatch<Value, Patch> implements ChunkPatch<Value, Patch> {
readonly [ChunkPatchSym]: ChunkPatchSym = ChunkPatchSym
readonly [ChunkPatchValueSym]!: () => Value
readonly [ChunkPatchPatchSym]!: () => Patch
}

export class AppendChunkPatch<Value, Patch> extends BaseChunkPatch<Value, Patch> {
readonly _tag = "Append"
constructor(readonly values: Chunk<Value>) {
super()
}
}

export class SliceChunkPatch<Value, Patch> extends BaseChunkPatch<Value, Patch> {
readonly _tag = "Slice"
constructor(readonly from: number, readonly until: number) {
super()
}
}

export class UpdateChunkPatch<Value, Patch> extends BaseChunkPatch<Value, Patch> {
readonly _tag = "Update"
constructor(readonly index: number, readonly patch: Patch) {
super()
}
}

export class AndThenChunkPatch<Value, Patch> extends BaseChunkPatch<Value, Patch> {
readonly _tag = "AndThen"
constructor(readonly first: ChunkPatch<Value, Patch>, readonly second: ChunkPatch<Value, Patch>) {
super()
}
}

export class EmptyChunkPatch<Value, Patch> extends BaseChunkPatch<Value, Patch> {
readonly _tag = "Empty"
}

export type ChunkPatchInstruction =
| AppendChunkPatch<any, any>
| SliceChunkPatch<any, any>
| UpdateChunkPatch<any, any>
| AndThenChunkPatch<any, any>
| EmptyChunkPatch<any, any>

/**
* @tsplus macro identity
*/
export function chunkPatchInstruction<Value, Patch>(
self: ChunkPatch<Value, Patch>
): ChunkPatchInstruction {
// @ts-expect-error
return self
}
6 changes: 6 additions & 0 deletions packages/core/_src/io/Differ/ChunkPatch/operations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// codegen:start {preset: barrel, include: ./operations/*.ts, prefix: "@effect/core/io/Differ/ChunkPatch"}
export * from "@effect/core/io/Differ/ChunkPatch/operations/apply"
export * from "@effect/core/io/Differ/ChunkPatch/operations/combine"
export * from "@effect/core/io/Differ/ChunkPatch/operations/diff"
export * from "@effect/core/io/Differ/ChunkPatch/operations/empty"
// codegen:end
52 changes: 52 additions & 0 deletions packages/core/_src/io/Differ/ChunkPatch/operations/apply.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import type { ChunkPatch } from "@effect/core/io/Differ/ChunkPatch/definition"
import { chunkPatchInstruction } from "@effect/core/io/Differ/ChunkPatch/definition"

/**
* Applies a chunk patch to a chunk of values to produce a new chunk of
* values which represents the original chunk of values updated with the
* changes described by this patch.
*
* @tsplus static effect/core/io/Differ.ChunkPatch.Aspects apply
* @tsplus pipeable effect/core/io/Differ.ChunkPatch apply
*/
export function apply<Value, Patch>(oldValue: Chunk<Value>, differ: Differ<Value, Patch>) {
return (self: ChunkPatch<Value, Patch>): Chunk<Value> => applyLoop(differ, oldValue, List(self))
}

/**
* @tsplus tailRec
*/
function applyLoop<Value, Patch>(
differ: Differ<Value, Patch>,
chunk: Chunk<Value>,
patches: List<ChunkPatch<Value, Patch>>
): Chunk<Value> {
if (patches.isNil()) {
return chunk
}
const patch = chunkPatchInstruction(patches.head)
const nextPatches = patches.tail
switch (patch._tag) {
case "Append": {
return applyLoop(differ, chunk.concat(patch.values), nextPatches)
}
case "AndThen": {
return applyLoop(differ, chunk, nextPatches.prepend(patch.second).prepend(patch.first))
}
case "Empty": {
return applyLoop(differ, chunk, nextPatches)
}
case "Slice": {
return applyLoop(
differ,
Chunk.from(chunk.toArray.slice(patch.from, patch.until)),
nextPatches
)
}
case "Update": {
const array = chunk.toArray
array[patch.index] = differ.patch(patch.patch, array[patch.index]!)
return applyLoop(differ, Chunk.from(array), nextPatches)
}
}
}
14 changes: 14 additions & 0 deletions packages/core/_src/io/Differ/ChunkPatch/operations/combine.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import type { ChunkPatch } from "@effect/core/io/Differ/ChunkPatch/definition"
import { AndThenChunkPatch } from "@effect/core/io/Differ/ChunkPatch/definition"

/**
* Combines two chunk patches to produce a new chunk patch that describes
* applying their changes sequentially.
*
* @tsplus static effect/core/io/Differ.ChunkPatch.Aspects combine
* @tsplus pipeable effect/core/io/Differ.ChunkPatch combine
*/
export function combine<Value, Patch>(that: ChunkPatch<Value, Patch>) {
return (self: ChunkPatch<Value, Patch>): ChunkPatch<Value, Patch> =>
new AndThenChunkPatch(self, that)
}
37 changes: 37 additions & 0 deletions packages/core/_src/io/Differ/ChunkPatch/operations/diff.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import type { ChunkPatch } from "@effect/core/io/Differ/ChunkPatch/definition"
import {
AppendChunkPatch,
SliceChunkPatch,
UpdateChunkPatch
} from "@effect/core/io/Differ/ChunkPatch/definition"

/**
* Constructs a chunk patch from a new and old chunk of values and a differ
* for the values.
*
* @tsplus static effect/core/io/Differ.ChunkPatch.Ops diff
*/
export function diff<Value, Patch>(
oldValue: Chunk<Value>,
newValue: Chunk<Value>,
differ: Differ<Value, Patch>
): ChunkPatch<Value, Patch> {
let i = 0
let patch = Differ.ChunkPatch.empty<Value, Patch>()
while (i < oldValue.length && i < newValue.length) {
const oldElement = oldValue.unsafeGet(i)
const newElement = newValue.unsafeGet(i)
const valuePatch = differ.diff(oldElement, newElement)
if (!Equals.equals(valuePatch, differ.empty)) {
patch = patch.combine(new UpdateChunkPatch(i, valuePatch))
}
i = i + 1
}
if (i < oldValue.length) {
patch = patch.combine(new SliceChunkPatch(0, i))
}
if (i < newValue.length) {
patch = patch.combine(new AppendChunkPatch(newValue.drop(i)))
}
return patch
}
11 changes: 11 additions & 0 deletions packages/core/_src/io/Differ/ChunkPatch/operations/empty.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { ChunkPatch } from "@effect/core/io/Differ/ChunkPatch/definition"
import { EmptyChunkPatch } from "@effect/core/io/Differ/ChunkPatch/definition"

/**
* Constructs an empty chunk patch.
*
* @tsplus static effect/core/io/Differ.ChunkPatch.Ops empty
*/
export function empty<Value, Patch>(): ChunkPatch<Value, Patch> {
return new EmptyChunkPatch()
}
4 changes: 4 additions & 0 deletions packages/core/_src/io/Differ/HashMapPatch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// codegen:start {preset: barrel, include: ./HashMapPatch/*.ts, prefix: "@effect/core/io/Differ"}
export * from "@effect/core/io/Differ/HashMapPatch/definition"
export * from "@effect/core/io/Differ/HashMapPatch/operations"
// codegen:end
113 changes: 113 additions & 0 deletions packages/core/_src/io/Differ/HashMapPatch/definition.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
export const HashMapPatchSym = Symbol.for("@effect/core/io/Differ.HashMapPatch")
export type HashMapPatchSym = typeof HashMapPatchSym

export const HashMapPatchKeySym = Symbol.for("@effect/core/io/Differ.HashMapPatch.Key")
export type HashMapPatchKeySym = typeof HashMapPatchKeySym

export const HashMapPatchValueSym = Symbol.for("@effect/core/io/Differ.HashMapPatch.Value")
export type HashMapPatchValueSym = typeof HashMapPatchValueSym

export const HashMapPatchPatchSym = Symbol.for("@effect/core/io/Differ.HashMapPatch.Patch")
export type HashMapPatchPatchSym = typeof HashMapPatchPatchSym

/**
* A patch which describes updates to a map of keys and values.
*
* @tsplus type effect/core/io/Differ.HashMapPatch
*/
export interface HashMapPatch<Key, Value, Patch> {
readonly [HashMapPatchSym]: HashMapPatchSym
readonly [HashMapPatchKeySym]: () => Key
readonly [HashMapPatchValueSym]: () => Value
readonly [HashMapPatchPatchSym]: () => Patch
}

/**
* @tsplus type effect/core/io/Differ.HashMapPatch.Ops
*/
export interface HashMapPatchOps {
readonly $: HashMapPatchAspects
}
/**
* @tsplus static effect/core/io/Differ.Ops HashMapPatch
*/
export const HashMapPatch: HashMapPatchOps = {
$: {}
}

/**
* @tsplus type effect/core/io/Differ.HashMapPatch.Aspects
*/
export interface HashMapPatchAspects {}

/**
* @tsplus unify effect/core/io/Differ.HashMapPatch
*/
export function unifyHashMapPatch<X extends HashMapPatch<any, any, any>>(self: X): HashMapPatch<
[X] extends [{ [HashMapPatchKeySym]: () => infer Key }] ? Key : never,
[X] extends [{ [HashMapPatchValueSym]: () => infer Value }] ? Value : never,
[X] extends [{ [HashMapPatchPatchSym]: () => infer Patch }] ? Patch : never
> {
return self
}

export abstract class BaseHashMapPatch<Key, Value, Patch>
implements HashMapPatch<Key, Value, Patch>
{
readonly [HashMapPatchSym]: HashMapPatchSym = HashMapPatchSym
readonly [HashMapPatchKeySym]!: () => Key
readonly [HashMapPatchValueSym]!: () => Value
readonly [HashMapPatchPatchSym]!: () => Patch
}

export class AddHashMapPatch<Key, Value, Patch> extends BaseHashMapPatch<Key, Value, Patch> {
readonly _tag = "Add"
constructor(readonly key: Key, readonly value: Value) {
super()
}
}

export class RemoveHashMapPatch<Key, Value, Patch> extends BaseHashMapPatch<Key, Value, Patch> {
readonly _tag = "Remove"
constructor(readonly key: Key) {
super()
}
}

export class UpdateHashMapPatch<Key, Value, Patch> extends BaseHashMapPatch<Key, Value, Patch> {
readonly _tag = "Update"
constructor(readonly key: Key, readonly patch: Patch) {
super()
}
}

export class EmptyHashMapPatch<Key, Value, Patch> extends BaseHashMapPatch<Key, Value, Patch> {
readonly _tag = "Empty"
}

export class AndThenHashMapPatch<Key, Value, Patch> extends BaseHashMapPatch<Key, Value, Patch> {
readonly _tag = "AndThen"
constructor(
readonly first: HashMapPatch<Key, Value, Patch>,
readonly second: HashMapPatch<Key, Value, Patch>
) {
super()
}
}

export type HashMapPatchInstruction =
| AddHashMapPatch<any, any, any>
| RemoveHashMapPatch<any, any, any>
| UpdateHashMapPatch<any, any, any>
| EmptyHashMapPatch<any, any, any>
| AndThenHashMapPatch<any, any, any>

/**
* @tsplus macro identity
*/
export function hashMapPatchInstruction<Key, Value, Patch>(
self: HashMapPatch<Key, Value, Patch>
): HashMapPatchInstruction {
// @ts-expect-error
return self
}
6 changes: 6 additions & 0 deletions packages/core/_src/io/Differ/HashMapPatch/operations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// codegen:start {preset: barrel, include: ./operations/*.ts, prefix: "@effect/core/io/Differ/HashMapPatch"}
export * from "@effect/core/io/Differ/HashMapPatch/operations/apply"
export * from "@effect/core/io/Differ/HashMapPatch/operations/combine"
export * from "@effect/core/io/Differ/HashMapPatch/operations/diff"
export * from "@effect/core/io/Differ/HashMapPatch/operations/empty"
// codegen:end
Loading