<N, E, T extends Kind = "directed">(
graph: Graph<N, E, T> | MutableGraph<N, E, T>
): booleanChecks whether the graph is acyclic (contains no cycles).
Details
Uses depth-first search to detect back edges, which indicate cycles. For directed graphs, any back edge creates a cycle. For undirected graphs, a back edge that doesn't go to the immediate parent creates a cycle.
Example (Checking cycles)
import { Graph } from "effect"
// Acyclic directed graph (DAG)
const dag = Graph.directed<string, string>((mutable) => {
const a = Graph.addNode(mutable, "A")
const b = Graph.addNode(mutable, "B")
const c = Graph.addNode(mutable, "C")
Graph.addEdge(mutable, a, b, "A->B")
Graph.addEdge(mutable, b, c, "B->C")
})
console.log(Graph.isAcyclic(dag)) // true
// Cyclic directed graph
const cyclic = Graph.directed<string, string>((mutable) => {
const a = Graph.addNode(mutable, "A")
const b = Graph.addNode(mutable, "B")
Graph.addEdge(mutable, a, b, "A->B")
Graph.addEdge(mutable, b, a, "B->A") // Creates cycle
})
console.log(Graph.isAcyclic(cyclic)) // falseexport const const isAcyclic: <
N,
E,
T extends Kind = "directed"
>(
graph: Graph<N, E, T> | MutableGraph<N, E, T>
) => boolean
Checks whether the graph is acyclic (contains no cycles).
Details
Uses depth-first search to detect back edges, which indicate cycles.
For directed graphs, any back edge creates a cycle. For undirected graphs,
a back edge that doesn't go to the immediate parent creates a cycle.
Example (Checking cycles)
import { Graph } from "effect"
// Acyclic directed graph (DAG)
const dag = Graph.directed<string, string>((mutable) => {
const a = Graph.addNode(mutable, "A")
const b = Graph.addNode(mutable, "B")
const c = Graph.addNode(mutable, "C")
Graph.addEdge(mutable, a, b, "A->B")
Graph.addEdge(mutable, b, c, "B->C")
})
console.log(Graph.isAcyclic(dag)) // true
// Cyclic directed graph
const cyclic = Graph.directed<string, string>((mutable) => {
const a = Graph.addNode(mutable, "A")
const b = Graph.addNode(mutable, "B")
Graph.addEdge(mutable, a, b, "A->B")
Graph.addEdge(mutable, b, a, "B->A") // Creates cycle
})
console.log(Graph.isAcyclic(cyclic)) // false
isAcyclic = <function (type parameter) N in <N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>): booleanN, function (type parameter) E in <N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>): booleanE, function (type parameter) T in <N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>): booleanT extends type Kind = "directed" | "undirected"Graph type for distinguishing directed and undirected graphs.
When to use
Use when writing graph-polymorphic types or helpers that need to preserve
whether a graph is directed or undirected.
Kind = "directed">(
graph: Graph<N, E, T> | MutableGraph<N, E, T>graph: interface Graph<out N, out E, T extends Kind = "directed">Immutable graph interface.
When to use
Use as the immutable graph model for code that queries, traverses,
transforms, or analyzes graph structure without mutating it.
Graph<function (type parameter) N in <N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>): booleanN, function (type parameter) E in <N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>): booleanE, function (type parameter) T in <N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>): booleanT> | interface MutableGraph<out N, out E, T extends Kind = "directed">Mutable graph interface.
When to use
Use when adding, removing, or updating nodes and edges inside a graph
mutation scope.
MutableGraph<function (type parameter) N in <N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>): booleanN, function (type parameter) E in <N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>): booleanE, function (type parameter) T in <N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>): booleanT>
): boolean => {
// Use existing cycle flag if available
if (import OptionOption.const isSome: <A>(
self: Option<A>
) => self is Some<A>
Checks whether an Option contains a value (Some).
When to use
Use when you need to branch on a present Option before accessing .value.
Details
- Acts as a type guard, narrowing to
Some<A>
Example (Checking for Some)
import { Option } from "effect"
console.log(Option.isSome(Option.some(1)))
// Output: true
console.log(Option.isSome(Option.none()))
// Output: false
isSome(graph: Graph<N, E, T> | MutableGraph<N, E, T>graph.Proto<out N, out E>.acyclic: Option.Option<boolean>acyclic)) {
return graph: Graph<N, E, T> | MutableGraph<N, E, T>graph.Proto<out N, out E>.acyclic: Option.Option<boolean>(property) Proto<out N, out E>.acyclic: {
_tag: "Some";
_op: "Some";
value: A;
valueOrUndefined: A;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
toString: () => string;
toJSON: () => unknown;
}
acyclic.Some<boolean>.value: booleanvalue
}
if (graph: Graph<N, E, T> | MutableGraph<N, E, T>graph.type: T extends Kind = "directed"type === "undirected") {
const const visited: Set<number>visited = new var Set: SetConstructor
new <number>(iterable?: Iterable<number> | null | undefined) => Set<number> (+1 overload)
Set<type NodeIndex = numberNode index for node identification using plain numbers.
When to use
Use when storing or passing the stable identifier of a graph node between
Graph operations.
Details
addNode allocates node identifiers from the graph's next node index.
Gotchas
A NodeIndex is an identifier, not an array offset. Removed node identifiers
are not reused.
NodeIndex>()
for (const const startNode: numberstartNode of graph: Graph<N, E, T> | MutableGraph<N, E, T>graph.Proto<out N, out E>.nodes: Map<NodeIndex, N>nodes.Map<number, N>.keys(): MapIterator<number>Returns an iterable of keys in the map
keys()) {
if (const visited: Set<number>visited.Set<number>.has(value: number): booleanhas(const startNode: numberstartNode)) {
continue
}
const visited: Set<number>visited.Set<number>.add(value: number): Set<number>Appends a new element with a specified value to the end of the Set.
add(const startNode: numberstartNode)
const const stack: Array<{
node: NodeIndex
parent: NodeIndex | null
}>
stack: interface Array<T>Array<{ node: NodeIndexnode: type NodeIndex = numberNode index for node identification using plain numbers.
When to use
Use when storing or passing the stable identifier of a graph node between
Graph operations.
Details
addNode allocates node identifiers from the graph's next node index.
Gotchas
A NodeIndex is an identifier, not an array offset. Removed node identifiers
are not reused.
NodeIndex; parent: NodeIndex | nullparent: type NodeIndex = numberNode index for node identification using plain numbers.
When to use
Use when storing or passing the stable identifier of a graph node between
Graph operations.
Details
addNode allocates node identifiers from the graph's next node index.
Gotchas
A NodeIndex is an identifier, not an array offset. Removed node identifiers
are not reused.
NodeIndex | null }> = [{ node: numbernode: const startNode: numberstartNode, parent: number | nullparent: null }]
while (const stack: Array<{
node: NodeIndex
parent: NodeIndex | null
}>
stack.Array<T>.length: numberGets or sets the length of the array. This is a number one higher than the highest index in the array.
length > 0) {
const { const node: numbernode, const parent: number | nullparent } = const stack: Array<{
node: NodeIndex
parent: NodeIndex | null
}>
stack.function Array(): { node: NodeIndex; parent: NodeIndex | null } | undefinedRemoves the last element from an array and returns it.
If the array is empty, undefined is returned and the array is not modified.
pop()!
const const nodeNeighbors: number[]nodeNeighbors = const getUndirectedNeighbors: <N, E>(
graph:
| Graph<N, E, "undirected">
| MutableGraph<N, E, "undirected">,
nodeIndex: NodeIndex
) => Array<NodeIndex>
Get neighbors for undirected graphs by checking both adjacency and reverse adjacency.
For undirected graphs, we need to find the other endpoint of each edge incident to the node.
getUndirectedNeighbors(graph: Graph<N, E, T> | MutableGraph<N, E, T>graph as any, const node: numbernode)
for (const const neighbor: numberneighbor of const nodeNeighbors: number[]nodeNeighbors) {
if (!const visited: Set<number>visited.Set<number>.has(value: number): booleanhas(const neighbor: numberneighbor)) {
const visited: Set<number>visited.Set<number>.add(value: number): Set<number>Appends a new element with a specified value to the end of the Set.
add(const neighbor: numberneighbor)
const stack: Array<{
node: NodeIndex
parent: NodeIndex | null
}>
stack.function Array(...items: Array<{ node: NodeIndex; parent: NodeIndex | null }>): numberAppends new elements to the end of an array, and returns the new length of the array.
push({ node: numbernode: const neighbor: numberneighbor, parent: number | nullparent: const node: numbernode })
} else if (const neighbor: numberneighbor !== const parent: number | nullparent) {
graph: Graph<N, E, T> | MutableGraph<N, E, T>graph.Proto<out N, out E>.acyclic: Option.Option<boolean>acyclic = import OptionOption.const some: <A>(value: A) => Option<A>Wraps the given value into an Option to represent its presence.
When to use
Use to wrap a known present value as Option
- Returning a successful result from a partial function
Details
- Always returns
Some<A>
- Does not filter
null or undefined; use
fromNullishOr
for that
Example (Wrapping a value)
import { Option } from "effect"
// ┌─── Option<number>
// ▼
const value = Option.some(1)
console.log(value)
// Output: { _id: 'Option', _tag: 'Some', value: 1 }
some(false)
return false
}
}
}
}
graph: Graph<N, E, T> | MutableGraph<N, E, T>graph.Proto<out N, out E>.acyclic: Option.Option<boolean>acyclic = import OptionOption.const some: <A>(value: A) => Option<A>Wraps the given value into an Option to represent its presence.
When to use
Use to wrap a known present value as Option
- Returning a successful result from a partial function
Details
- Always returns
Some<A>
- Does not filter
null or undefined; use
fromNullishOr
for that
Example (Wrapping a value)
import { Option } from "effect"
// ┌─── Option<number>
// ▼
const value = Option.some(1)
console.log(value)
// Output: { _id: 'Option', _tag: 'Some', value: 1 }
some(true)
return true
}
// Stack-safe DFS cycle detection using iterative approach
const const visited: Set<number>visited = new var Set: SetConstructor
new <number>(iterable?: Iterable<number> | null | undefined) => Set<number> (+1 overload)
Set<type NodeIndex = numberNode index for node identification using plain numbers.
When to use
Use when storing or passing the stable identifier of a graph node between
Graph operations.
Details
addNode allocates node identifiers from the graph's next node index.
Gotchas
A NodeIndex is an identifier, not an array offset. Removed node identifiers
are not reused.
NodeIndex>()
const const recursionStack: Set<number>recursionStack = new var Set: SetConstructor
new <number>(iterable?: Iterable<number> | null | undefined) => Set<number> (+1 overload)
Set<type NodeIndex = numberNode index for node identification using plain numbers.
When to use
Use when storing or passing the stable identifier of a graph node between
Graph operations.
Details
addNode allocates node identifiers from the graph's next node index.
Gotchas
A NodeIndex is an identifier, not an array offset. Removed node identifiers
are not reused.
NodeIndex>()
// Stack entry: [node, neighbors, neighborIndex, isFirstVisit]
type type DfsStackEntry = [
number,
number[],
number,
boolean
]
DfsStackEntry = [type NodeIndex = numberNode index for node identification using plain numbers.
When to use
Use when storing or passing the stable identifier of a graph node between
Graph operations.
Details
addNode allocates node identifiers from the graph's next node index.
Gotchas
A NodeIndex is an identifier, not an array offset. Removed node identifiers
are not reused.
NodeIndex, interface Array<T>Array<type NodeIndex = numberNode index for node identification using plain numbers.
When to use
Use when storing or passing the stable identifier of a graph node between
Graph operations.
Details
addNode allocates node identifiers from the graph's next node index.
Gotchas
A NodeIndex is an identifier, not an array offset. Removed node identifiers
are not reused.
NodeIndex>, number, boolean]
// Get all nodes to handle disconnected components
for (const const startNode: numberstartNode of graph: Graph<N, E, T> | MutableGraph<N, E, T>graph.Proto<out N, out E>.nodes: Map<NodeIndex, N>nodes.Map<number, N>.keys(): MapIterator<number>Returns an iterable of keys in the map
keys()) {
if (const visited: Set<number>visited.Set<number>.has(value: number): booleanhas(const startNode: numberstartNode)) {
continue // Already processed this component
}
// Iterative DFS with explicit stack
const const stack: DfsStackEntry[]stack: interface Array<T>Array<type DfsStackEntry = [
number,
number[],
number,
boolean
]
DfsStackEntry> = [[const startNode: numberstartNode, [], 0, true]]
while (const stack: DfsStackEntry[]stack.Array<T>.length: numberGets or sets the length of the array. This is a number one higher than the highest index in the array.
length > 0) {
const [const node: numbernode, const neighbors: number[]neighbors, const neighborIndex: numberneighborIndex, const isFirstVisit: booleanisFirstVisit] = const stack: DfsStackEntry[]stack[const stack: DfsStackEntry[]stack.Array<T>.length: numberGets or sets the length of the array. This is a number one higher than the highest index in the array.
length - 1]
// First visit to this node
if (const isFirstVisit: booleanisFirstVisit) {
if (const recursionStack: Set<number>recursionStack.Set<number>.has(value: number): booleanhas(const node: numbernode)) {
// Back edge found - cycle detected
graph: Graph<N, E, T> | MutableGraph<N, E, T>graph.Proto<out N, out E>.acyclic: Option.Option<boolean>acyclic = import OptionOption.const some: <A>(value: A) => Option<A>Wraps the given value into an Option to represent its presence.
When to use
Use to wrap a known present value as Option
- Returning a successful result from a partial function
Details
- Always returns
Some<A>
- Does not filter
null or undefined; use
fromNullishOr
for that
Example (Wrapping a value)
import { Option } from "effect"
// ┌─── Option<number>
// ▼
const value = Option.some(1)
console.log(value)
// Output: { _id: 'Option', _tag: 'Some', value: 1 }
some(false)
return false
}
if (const visited: Set<number>visited.Set<number>.has(value: number): booleanhas(const node: numbernode)) {
const stack: DfsStackEntry[]stack.Array<DfsStackEntry>.pop(): DfsStackEntry | undefinedRemoves the last element from an array and returns it.
If the array is empty, undefined is returned and the array is not modified.
pop()
continue
}
const visited: Set<number>visited.Set<number>.add(value: number): Set<number>Appends a new element with a specified value to the end of the Set.
add(const node: numbernode)
const recursionStack: Set<number>recursionStack.Set<number>.add(value: number): Set<number>Appends a new element with a specified value to the end of the Set.
add(const node: numbernode)
// Get neighbors for this node
const const nodeNeighbors: number[]nodeNeighbors = const getDirectedNeighbors: <N, E>(
graph:
| Graph<N, E, "directed">
| MutableGraph<N, E, "directed">,
nodeIndex: NodeIndex,
direction: Direction
) => Array<NodeIndex>
getDirectedNeighbors(
graph: Graph<N, E, T> | MutableGraph<N, E, T>graph as interface Graph<out N, out E, T extends Kind = "directed">Immutable graph interface.
When to use
Use as the immutable graph model for code that queries, traverses,
transforms, or analyzes graph structure without mutating it.
Graph<function (type parameter) N in <N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>): booleanN, function (type parameter) E in <N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>): booleanE, "directed"> | interface MutableGraph<out N, out E, T extends Kind = "directed">Mutable graph interface.
When to use
Use when adding, removing, or updating nodes and edges inside a graph
mutation scope.
MutableGraph<function (type parameter) N in <N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>): booleanN, function (type parameter) E in <N, E, T extends Kind = "directed">(graph: Graph<N, E, T> | MutableGraph<N, E, T>): booleanE, "directed">,
const node: numbernode,
"outgoing"
)
const stack: DfsStackEntry[]stack[const stack: DfsStackEntry[]stack.Array<T>.length: numberGets or sets the length of the array. This is a number one higher than the highest index in the array.
length - 1] = [const node: numbernode, const nodeNeighbors: number[]nodeNeighbors, 0, false]
continue
}
// Process next neighbor
if (const neighborIndex: numberneighborIndex < const neighbors: number[]neighbors.Array<T>.length: numberGets or sets the length of the array. This is a number one higher than the highest index in the array.
length) {
const const neighbor: numberneighbor = const neighbors: number[]neighbors[const neighborIndex: numberneighborIndex]
const stack: DfsStackEntry[]stack[const stack: DfsStackEntry[]stack.Array<T>.length: numberGets or sets the length of the array. This is a number one higher than the highest index in the array.
length - 1] = [const node: numbernode, const neighbors: number[]neighbors, const neighborIndex: numberneighborIndex + 1, false]
if (const recursionStack: Set<number>recursionStack.Set<number>.has(value: number): booleanhas(const neighbor: numberneighbor)) {
// Back edge found - cycle detected
graph: Graph<N, E, T> | MutableGraph<N, E, T>graph.Proto<out N, out E>.acyclic: Option.Option<boolean>acyclic = import OptionOption.const some: <A>(value: A) => Option<A>Wraps the given value into an Option to represent its presence.
When to use
Use to wrap a known present value as Option
- Returning a successful result from a partial function
Details
- Always returns
Some<A>
- Does not filter
null or undefined; use
fromNullishOr
for that
Example (Wrapping a value)
import { Option } from "effect"
// ┌─── Option<number>
// ▼
const value = Option.some(1)
console.log(value)
// Output: { _id: 'Option', _tag: 'Some', value: 1 }
some(false)
return false
}
if (!const visited: Set<number>visited.Set<number>.has(value: number): booleanhas(const neighbor: numberneighbor)) {
const stack: DfsStackEntry[]stack.Array<DfsStackEntry>.push(...items: DfsStackEntry[]): numberAppends new elements to the end of an array, and returns the new length of the array.
push([const neighbor: numberneighbor, [], 0, true])
}
} else {
// Done with this node - backtrack
const recursionStack: Set<number>recursionStack.Set<number>.delete(value: number): booleanRemoves a specified value from the Set.
delete(const node: numbernode)
const stack: DfsStackEntry[]stack.Array<DfsStackEntry>.pop(): DfsStackEntry | undefinedRemoves the last element from an array and returns it.
If the array is empty, undefined is returned and the array is not modified.
pop()
}
}
}
// Cache the result
graph: Graph<N, E, T> | MutableGraph<N, E, T>graph.Proto<out N, out E>.acyclic: Option.Option<boolean>acyclic = import OptionOption.const some: <A>(value: A) => Option<A>Wraps the given value into an Option to represent its presence.
When to use
Use to wrap a known present value as Option
- Returning a successful result from a partial function
Details
- Always returns
Some<A>
- Does not filter
null or undefined; use
fromNullishOr
for that
Example (Wrapping a value)
import { Option } from "effect"
// ┌─── Option<number>
// ▼
const value = Option.some(1)
console.log(value)
// Output: { _id: 'Option', _tag: 'Some', value: 1 }
some(true)
return true
}