Scott Nimrod
Scott Nimrod

Reputation: 11595

How do I initialize a value that's based on a Recursive Type Alias?

How do I initialize a value that's based on a Recursive Type Alias?

type alias ContentProvider =
    { profile : Profile
    , topics : List Topic
    , links : Links
    , subscribers : Subscribers
    }


type Subscribers
    = Subscribers (List ContentProvider)

My attempt to initialize the value fails:

contentProvider1 : ContentProvider
contentProvider1 =
    ContentProvider profile1 topics contentProvider1Links (Subscribers [ contentProvider2, contentProvider3 ])

Note that the last argument is causing the error:

(Subscribers [ contentProvider2, contentProvider3 ])

contentProvider1 is defined in terms of itself in a sneaky way, causing an infinite loop. - The following definitions depend directly on each other:

┌─────┐
│    contentProvider1
│     ↓
│    contentProvider2
│     ↓
│    contentProvider3
└─────┘

I'm referencing this documentation. However, I do not understand why I am still getting a compiler error.

Appendix:

contentProvider1 : ContentProvider
contentProvider1 =
    ContentProvider profile1 topics contentProvider1Links (Subscribers [ contentProvider2 ])


contentProvider2 : ContentProvider
contentProvider2 =
    ContentProvider profile2 topics contentProvider2Links (Subscribers [ contentProvider1, contentProvider3 ])


contentProvider3 : ContentProvider
contentProvider3 =
    ContentProvider profile3 topics contentProvider3Links (Subscribers [ contentProvider1, contentProvider2 ])

Upvotes: 1

Views: 248

Answers (2)

Chad Gilbert
Chad Gilbert

Reputation: 36375

Your code is syntactically correct, and prior to Elm 0.18, it would have compiled. But when you ran the program you would have ended up in either a stack overflow, an infinite loop (because of tail call recursion optimization), or a runtime exception because of a compiler bug that has since been fixed.

Consider what the runtime would have to go through to expand contentProvider1:

  1. Expand contentProvider1, which has a reference to contentProvider2
  2. Expand contentProvider2, which has a reference to contentProvider1
  3. Expand contentProvider1, which has a reference to contentProvider2
  4. and so on, to eternity

What you are experiencing is Elm's super helpful compiler saving you from code that wouldn't have worked anyway. It is still possible to write a non-halting function (and always will be), but Elm's compiler now checks for some common cases that would have otherwise blown up at runtime.

Here is some documentation that more thoroughly explains the reasoning behind this compiler error.

Upvotes: 3

sean helvey
sean helvey

Reputation: 27

contentProvider2 and contentProvider3 need to be defined like Chad Gilbert said, but the issue here isn't your mutually recursive type alias, it is the circular value reference that is not possible. Here is a simplified example that compiles:

type alias ContentProvider =
    { profile : String
    , topics : List String
    , links : String
    , subscribers : Subscribers
    }

type Subscribers = 
    Subscribers (List ContentProvider)

contentProvider1 : ContentProvider
contentProvider1 =
    ContentProvider "profile" ["art", "science"] "test" (Subscribers [contentProvider2])

contentProvider2 : ContentProvider
contentProvider2 =
    ContentProvider "profile" ["art", "science"] "test" (Subscribers [])

Upvotes: 1

Related Questions