Как инициализировать значение, основанное на псевдониме рекурсивного типа?

Как инициализировать значение, основанное на псевдониме рекурсивного типа?

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


type Subscribers
    = Subscribers (List ContentProvider)

Моя попытка инициализировать значение не удалась:

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

Обратите внимание, что последний аргумент вызывает ошибку:

(Subscribers [ contentProvider2, contentProvider3 ])

contentProvider1 определяется в терминах самого себя хитрым способом, вызывая бесконечный цикл. - Следующие определения напрямую зависят друг от друга:

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

Я ссылаюсь на эту документацию. Однако я не понимаю, почему я все еще получаю ошибку компилятора.

Приложение:

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 ])

2 ответа

Ваш код синтаксически правильный, и до версии Elm 0.18 он компилировался бы. Но когда вы запустите программу, вы окажетесь либо в переполнении стека, либо в бесконечном цикле (из-за оптимизации рекурсии хвостового вызова), либо в исключительной ситуации времени выполнения из-за ошибки компилятора, которая с тех пор была исправлена.

Подумайте, через что должна пройти среда выполнения, чтобы расширить contentProvider1:

  1. расширять contentProvider1, который имеет ссылку на contentProvider2
  2. расширять contentProvider2, который имеет ссылку на contentProvider1
  3. расширять contentProvider1, который имеет ссылку на contentProvider2
  4. и так до бесконечности

Что вы испытываете, так это супер полезный компилятор Elm, спасающий вас от кода, который в любом случае не сработал бы. Все еще возможно написать функцию без остановки ( и всегда будет), но компилятор Elm теперь проверяет некоторые распространенные случаи, которые в противном случае могли бы сработать во время выполнения.

Вот некоторая документация, которая более подробно объясняет причину этой ошибки компилятора.

contentProvider2 и contentProvider3 должны быть определены, как сказал Чад Гилберт, но проблема здесь не в вашем взаимно рекурсивном псевдониме типа, а в круговой ссылке на значение, которая невозможна. Вот упрощенный пример, который компилируется:

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 [])
Другие вопросы по тегам