/**
* @since 1.0.0
*/
import * as Dual from "@effect/data/Function"
import { NodeInspectSymbol, toJSON, toString } from "@effect/data/Inspectable"
import type { Inspectable } from "@effect/data/Inspectable"
import type { Pipeable } from "@effect/data/Pipeable"
import { pipeArguments } from "@effect/data/Pipeable"
const TypeId: unique symbol = Symbol.for("@effect/data/MutableList") as TypeId
/**
* @since 1.0.0
* @category symbol
*/
export type TypeId = typeof TypeId
/**
* @since 1.0.0
* @category model
*/
export interface MutableList extends Iterable, Pipeable, Inspectable {
readonly [TypeId]: TypeId
/** @internal */
head: LinkedListNode | undefined
/** @internal */
tail: LinkedListNode | undefined
}
const MutableListProto: Omit, "head" | "tail"> = {
[TypeId]: TypeId,
[Symbol.iterator](this: MutableList): Iterator {
let done = false
let head: LinkedListNode | undefined = this.head
return {
next() {
if (done) {
return this.return!()
}
if (head == null) {
done = true
return this.return!()
}
const value = head.value
head = head.next
return { done, value }
},
return(value?: unknown) {
if (!done) {
done = true
}
return { done: true, value }
}
}
},
toString() {
return toString(this.toJSON())
},
toJSON() {
return {
_id: "MutableList",
values: Array.from(this).map(toJSON)
}
},
[NodeInspectSymbol]() {
return this.toJSON()
},
pipe() {
return pipeArguments(this, arguments)
}
}
interface MutableListImpl extends MutableList {
_length: number
}
/** @internal */
class LinkedListNode {
removed = false
prev: LinkedListNode | undefined = undefined
next: LinkedListNode | undefined = undefined
constructor(readonly value: T) {}
}
/**
* Creates an empty `MutableList`.
*
* @since 1.0.0
* @category constructors
*/
export const empty = (): MutableList => {
const list = Object.create(MutableListProto)
list.head = undefined
list.tail = undefined
list._length = 0
return list
}
/**
* Creates a new `MutableList` from an `Iterable`.
*
* @since 1.0.0
* @category constructors
*/
export const fromIterable = (iterable: Iterable): MutableList => {
const list = empty()
for (const element of iterable) {
append(list, element)
}
return list
}
/**
* Creates a new `MutableList` from the specified elements.
*
* @since 1.0.0
* @category constructors
*/
export const make = (...elements: ReadonlyArray): MutableList => fromIterable(elements)
/**
* Returns `true` if the list contains zero elements, `false`, otherwise.
*
* @since 1.0.0
* @category getters
*/
export const isEmpty = (self: MutableList): boolean => length(self) === 0
/**
* Returns the length of the list.
*
* @since 1.0.0
* @category getters
*/
export const length = (self: MutableList): number => (self as MutableListImpl)._length
/**
* Returns the last element of the list, if it exists.
*
* @since 1.0.0
* @category getters
*/
export const tail = (self: MutableList): A | undefined => self.tail === undefined ? undefined : self.tail.value
/**
* Returns the first element of the list, if it exists.
*
* @since 1.0.0
* @category getters
*/
export const head = (self: MutableList): A | undefined => self.head === undefined ? undefined : self.head.value
/**
* Executes the specified function `f` for each element in the list.
*
* @since 1.0.0
* @category traversing
*/
export const forEach: {
(f: (element: A) => void): (self: MutableList) => void
(self: MutableList, f: (element: A) => void): void
} = Dual.dual<
(f: (element: A) => void) => (self: MutableList) => void,
(self: MutableList, f: (element: A) => void) => void
>(2, (self, f) => {
let current = self.head
while (current !== undefined) {
f(current.value)
current = current.next
}
})
/**
* Removes all elements from the doubly-linked list.
*
* @since 1.0.0
*/
export const reset = (self: MutableList): MutableList => {
;(self as MutableListImpl)._length = 0
self.head = undefined
self.tail = undefined
return self
}
/**
* Appends the specified element to the end of the `MutableList`.
*
* @category concatenating
* @since 1.0.0
*/
export const append: {
(value: A): (self: MutableList) => MutableList
(self: MutableList, value: A): MutableList
} = Dual.dual<
(value: A) => (self: MutableList) => MutableList,
(self: MutableList, value: A) => MutableList
>(2, (self: MutableList, value: A) => {
const node = new LinkedListNode(value)
if (self.head === undefined) {
self.head = node
}
if (self.tail === undefined) {
self.tail = node
} else {
self.tail.next = node
node.prev = self.tail
self.tail = node
}
;(self as MutableListImpl)._length += 1
return self
})
/**
* Removes the first value from the list and returns it, if it exists.
*
* @since 0.0.1
*/
export const shift = (self: MutableList): A | undefined => {
const head = self.head
if (head !== undefined) {
remove(self, head)
return head.value
}
return undefined
}
/**
* Removes the last value from the list and returns it, if it exists.
*
* @since 0.0.1
*/
export const pop = (self: MutableList): A | undefined => {
const tail = self.tail
if (tail !== undefined) {
remove(self, tail)
return tail.value
}
return undefined
}
/**
* Prepends the specified value to the beginning of the list.
*
* @category concatenating
* @since 1.0.0
*/
export const prepend: {
(value: A): (self: MutableList) => MutableList
(self: MutableList, value: A): MutableList
} = Dual.dual<
(value: A) => (self: MutableList) => MutableList,
(self: MutableList, value: A) => MutableList
>(2, (self: MutableList, value: A) => {
const node = new LinkedListNode(value)
node.next = self.head
if (self.head !== undefined) {
self.head.prev = node
}
self.head = node
if (self.tail === undefined) {
self.tail = node
}
;(self as MutableListImpl)._length += 1
return self
})
const remove = (self: MutableList, node: LinkedListNode): void => {
if (node.removed) {
return
}
node.removed = true
if (node.prev !== undefined && node.next !== undefined) {
node.prev.next = node.next
node.next.prev = node.prev
} else if (node.prev !== undefined) {
self.tail = node.prev
node.prev.next = undefined
} else if (node.next !== undefined) {
self.head = node.next
node.next.prev = undefined
} else {
self.tail = undefined
self.head = undefined
}
if ((self as MutableListImpl)._length > 0) {
;(self as MutableListImpl)._length -= 1
}
}