リストとか木構造とかってオブジェクトに相互の参照が入ってることがよくあります
DOM でも children と parentElement だったり nextElementSibling と previousElementSibling だったりがあります

ミュータブルなものなら簡単に実現できますが イミュータブルなデータだと相互に参照を入れられない気がします

普通に JavaScript でやると

const child1 = {
parent: null,
children: [],
prev: null,
next: null,
}
const child2 = {
parent: null,
children: [],
prev: null,
next: null,
}
const parent = {
parent: null,
children: [],
prev: null,
next: null,
}

child1.next = child2
child1.parent = parent

child2.prev = child1
child2.parent = parent

parent.children.push(child1, child2)

これがイミュータブルだとすると 参照をいれるところがこうなります

const child1_new = { ...child1, next: child2, parent }
const child2_new = { ...child2, prev: child1_new, parent }

こうしたときに child2_new.prev に child1_new が入っていますが child1_new.next は古い child2 です
イミュータブルしかできない言語だとこういう構造は扱わないのでしょうか

直接参照をもたせず 間接的に持たせて自分で参照するならできそうですが 手間が多いです

const parent_id = Math.random()
const child1_id = Math.random()
const child2_id = Math.random()

const parent = {
parent: null,
children: [child1_id, child2_id],
prev: null,
next: null,
}
const child1 = {
parent: parent_id,
children: [],
prev: null,
next: child2_id,
}
const child2 = {
parent: parent_id,
children: [],
prev: child1_id,
next: null,
}

const refs = {
[parent_id]: parent,
[child1_id]: child1,
[child2_id]: child2,
}

console.log(child1 === refs[refs[child1.next].prev])
// true

更新することも考えるとあまりやりたい方法ではないです



イミュータブルな言語でやり方をググってみると 関数にして遅延評価することで実現してる例がありました

const parent = {
parent: null,
children: [() => child1, () => child2],
prev: null,
next: null,
}
const child1 = {
parent: () => parent,
children: [],
prev: null,
next: () => child2,
}
const child2 = {
parent: () => parent,
children: [],
prev: () => child1,
next: null,
}

console.log(child1 === child1.next().prev())
// true

たしかに実行時に参照先が評価される言語なら関数を作る時点で参照できなくても大丈夫そうです
ただこれでも変更は簡単ではなく どこかを書き換えると参照先も変更が必要になり全体を作り直すことになります
なのでそういうデータ構造は持たず単一方向にするのが普通で なぜ循環する参照が必要になるのか聞かれてるような感じでした