"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
reading <https://elm-lang.org/0.19.0/bad-recursion> for more detailed advice,
especially if you actually do want mutually recursive values.

Ive asked about this on Slack, and got good advice (from @norpan) not to do it this way .

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.