@samitouri / QOS-React / commits / cb72726e38

Cleanup Stack impl

Originally I defined `Stack` as an interface to ensure both Node and Empty variants would have an identical API. But exporting an interface allows a developer to define other implementations, when we really want to ensure that a Stack is precisely a Node or Empty instance. This PR changes to exporting a union of `Stack = Node | Empty`, and makes the interface private to the module.

Joe Savona committed Apr 21, 2023 at 08:03 UTC cb72726e384ad0ac5db22d51d8a76d5ee121917d
1 file changed +20 -6
compiler/forget/src/Utils/Stack.ts
+20 -6
@@ -5,10 +5,24 @@
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 -export interface Stack<T> {
9 - push(value: T): Stack<T>;
8 +/**
9 + * An immutable stack data structure supporting O(1) push/pop operations.
10 + */
11 +export type Stack<T> = Node<T> | Empty<T>;
12 +
13 +// Static assertion that Stack<T> is a StackInterface<T>
14 +function _assertStackInterface<T>(stack: Stack<T>): void {
15 + let _: StackInterface<T> = stack;
16 +}
17 +
18 +// Internal interface to enforce consistent behavior btw Node/Empty variants
19 +// Note that we export a union rather than the interface so that it is impossible
20 +// to create additional variants: a Stack should always be exactly a Node or Empty
21 +// instance.
22 +interface StackInterface<T> {
23 + push(value: T): StackInterface<T>;
24
11 - pop(): Stack<T>;
25 + pop(): StackInterface<T>;
26
27 contains(value: T): boolean;
28
@@ -25,7 +39,7 @@ export function empty<T>(): Stack<T> {
39 return EMPTY as any;
40 }
41
28 -class Node<T> implements Stack<T> {
42 +class Node<T> implements StackInterface<T> {
43 #value: T;
44 #next: Stack<T>;
45
@@ -58,9 +72,9 @@ class Node<T> implements Stack<T> {
72 }
73 }
74
61 -class Empty<T> implements Stack<T> {
75 +class Empty<T> implements StackInterface<T> {
76 push(value: T): Stack<T> {
63 - return new Node(value, this);
77 + return new Node(value as T, this as Stack<T>);
78 }
79 pop(): Stack<T> {
80 return this;