Вы можете построить только циклическую неизменяемую строгую структуру данных формы, которая определяется во время компиляции. Я не собираюсь определять или доказывать это формально, но, интуитивно говоря, после создания структуры данных ее форма не изменится (поскольку она неизменна). Таким образом, вы не можете добавить в цикл. И если вы создаете какой-либо элемент цикла, вам нужно создать все другие элементы цикла одновременно, потому что у вас не может быть никакого висящего указателя.
Ocaml может делать то же, что и Haskell, но вам нужно задействовать модуль Lazy
! В отличие от Haskell, структуры данных ML являются строгими, если не указано иное. Ленивая структура данных имеет фрагменты типа 'a Lazy.t
. (ML в этом конкретном вопросе более точен, чем в Haskell.) Ленивые структуры данных позволяют строить циклы, используя предварительно висячие указатели (чьи связанные значения автоматически создаются при первом разыменовании указателя).
type 'a lazy_dlist_value =
| Dnil
| Dnode of 'a lazy_dlist_value * 'a * 'a lazy_dlist_value
and 'a lazy_dlist = 'a lazy_dlist_value Lazy.t
Другой распространенный способ иметь циклические структуры данных - это использовать изменяемые узлы. (На самом деле, сторонники строгого программирования могут рассматривать ленивые структуры данных как особый случай изменчивых структур данных, которые не слишком сильно нарушают ссылочную прозрачность.)
type 'a mutable_dlist_value =
| Dnil
| Dnode of 'a mutable_dlist_value * 'a * 'a mutable_dlist_value
and 'a mutable_dlist = 'a mutable_dlist_value ref
Циклические структуры данных в основном полезны, когда в них участвует хотя бы один изменяемый компонент, одна функция (замыкание) или иногда модули. Но у компилятора не было бы никаких причин применять это - циклические строгие неизменяемые структуры данных первого порядка - это просто особый случай, который иногда может быть полезен.