# "Tricky Recursion" - I am not sure why

I have this code

type ArcMsg
= Foo
| Bar

type alias ArcName =
String

type alias ArcMessages =
List ArcMsg

type Arc
= Arc String ArcMessages DelayedNode

type alias ArcList =
List Arc

type alias DelayedNode =
{ node : () -> Node }

type alias DelayedArcList =
{ arcList : () -> ArcList }

delayNode : Node -> DelayedNode
delayNode node =
DelayedNode (\() -> node)

delayArcList : ArcList -> DelayedArcList
delayArcList arcList =
DelayedArcList (\() -> arcList)

type alias Node =
{ name : String
, arcs : DelayedArcList
, timeout : Maybe Int
}

n1 =
Node "n1" (delayArcList [ a1, a2 ]) (Just 5)

n2 =
Node "n2" (delayArcList [ a2 ]) (Just 1)

a1 : Arc
a1 =
Arc "a1" [ Foo, Bar ] (delayNode n2)

a2 : Arc
a2 =
Arc "a2" [ Bar, Foo ] (delayNode n1)

and I get

CYCLIC DEFINITION - The `a1` definition is causing a very tricky infinite loop.

59| a1 =
#^^#
The `a1` value depends on itself through the following chain of definitions:

┌─────┐
│    #a1#
│     ↓
│    #n2#
│     ↓
│    #a2#
│     ↓
│    #n1#
└─────┘

#Hint#: The root problem is often a typo in some variable name, but I recommend
especially if you actually do want mutually recursive values.

My quest here is to understand why the thunks (() -> value) dont break the cycle. That seems to be the implication on the referenced page in the error loop. I would be grateful for some help!

thanks

The thunk creation cannot be extracted into a function, because a function will (eagerly/strictly) evaluate its arguments. If you inline the delayNode and delayArcList definitions, this should compile.

to reiterate:

makeLazy x = (\() -> x)

f (makeLazy x)

will not work, because x gets evaluated when it is passed to the function makeLazy. In contrast

f (\() -> x)

will not evaluate x until the thunk is forced.

Ah… Can’t thank you enough @folkertdev
Works fine -

I am very grateful.

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.