{"slug": "lazily-consuming-a-self-referential-linked-list", "title": "Lazily consuming a self-referential linked list", "summary": "The author experimented with implementing a lazily generated work queue for recursive algorithms like breadth-first search and grammar FIRST-Set computation, but found it outperformed by `Data.Sequence.Seq` in both space and time. They ultimately adopted a simpler, more memory-efficient approach using a monadic `WorkList` abstraction with explicit job sequencing, which processes jobs sequentially while maintaining state. The author plans to potentially release this abstraction as a small library if it proves useful.", "body_md": "This was a great suggestion, I ended up trying this, it works perfectly well when you sprinkle in strictness annotations.\nMy implementation, sadly, was still outperformed by Data.Sequence.Seq\nboth in terms of space and time.\nSure, algorithms I had in mind were something like breadth-first-search, dijktra, A*, FIRST-Set of a Grammar Nonterminal.\nAll of these problems have a recursive notion. E.g. to determine the First-Set of Nonterminal you must also determine the First Set of all Nonterminals that are reachable through the original.\nOr in my abstraction, any abstract “job” can issue any number of new work portions that arose from it’s processing. The function signature was very involved because I tried so hard to have a lazily generated work queue.\nI want to thank everyone involved for the suggestions, I had a blast trying it all and benchmarking some stuff, though nothing held up against an implementation that used Seq\n.\nI’m now doing this: j -> m (Seq j, a)\n, it is always possible to read the current job, perform a monadic action and produce a Sequence of new jobs that should be run afterwards. This is very boring (no infinitely lazy lists) but very memory and space-efficient, apparently.\nI think I might release a small library if this abstraction proves useful for me.\nWhat I’m doing currently looks like this:\nreachableVertices :: Graph Vertex -> Vertex -> Set Vertex\nreachableVertices graph start = WorkList.run (Set.singleton start) [start] $ do\nnode <- WorkList.current\nvisited <- State.get\nforM_ (neighbors graph node) $ \\ neighbor -> do\nunless (neighbor `Set.member` visited) $ WorkList.queue neighbor\nState.modify (Set.insert neighbor)\nthe do-notation-block would have this type:\nbfsDo :: WorkList Vertex (Set Vertex) ()\nthe first type argument is the job type, the second one is the state type and the third one is the monadic result type.\nUsing the WorkList.run\nfunction will run the monadic computation repeatedly until the job queue is empty and then yield the final state.\nrun :: Foldable f => s -> f q -> WorkList q s () -> s", "url": "https://wpnews.pro/news/lazily-consuming-a-self-referential-linked-list", "canonical_source": "https://discourse.haskell.org/t/lazily-consuming-a-self-referential-linked-list/14131#post_14", "published_at": "2026-05-21 14:14:00+00:00", "updated_at": "2026-05-22 08:52:11.255338+00:00", "lang": "en", "topics": ["developer-tools", "open-source", "research"], "entities": ["Data.Sequence.Seq", "WorkList"], "alternates": {"html": "https://wpnews.pro/news/lazily-consuming-a-self-referential-linked-list", "markdown": "https://wpnews.pro/news/lazily-consuming-a-self-referential-linked-list.md", "text": "https://wpnews.pro/news/lazily-consuming-a-self-referential-linked-list.txt", "jsonld": "https://wpnews.pro/news/lazily-consuming-a-self-referential-linked-list.jsonld"}}