Search code examples
haskellrecordcyclic-dependency

Solve cyclic dependencies in haskell data records


Imagine I want to write an application which deals with podcast feeds. To store the parsed information from such a feed, I would write something like this:

data Podcast = Podcast {
    podcastTitle :: String, -- ^ title of podcast
    episodes :: [Episode]   -- ^ list of episodes of podcast
    ...                     -- ^ some other fields
} deriving (Show)

data Episode = Episode {
    episodeTitle :: String, -- ^ title of episode
    podcast :: Podcast      -- ^ podcast this episode belongs to
    ...                     -- ^ some other fields
} deriving (Show)

The above data record definitions reflect a common 1:n relation between data types: A podcast has many episodes and the episode belongs to one podcast. Now I have problems defining such podcast: Do define a Podcast I already need the list of Episodes but to define an Episode entity I need the Podcast entity. It seems to me, that solving this cyclic dependency is impossible in haskell...

I also think that the above code is a relict from my programming in other languages. In the above style I would do it for example in python, but this programming language has a notion of state. In python I can first define a Podcast entity without episodes, initialize then all episodes with the defined Podcast entity and set then the episodes field of the podcast to the list of episodes.

My question: What is the haskell way to model the 1:n relation between podcasts and episodes?

Answers to questions in the comments:

Why must an episode reference a specific podcast? It would be nice to have a function

podcast :: Episode -> Podcast

which returns the episode's podcast whenever I need it. I know, that one solution would be to pass also the Podcast entity to each function for episodes, i.e. I replace each function

func1 :: Episode -> Something

where I would need the above podcast function with

func1 :: Podcast -> Episode -> Something

It would be just great, to write as little code as possible and not to have the necessity to carry the Podcast entity everywhere around.

Maybe I change my question a litte bit: It would be totally okay to define the Episode data record without the podcast field. How can implement

podcast :: Episode -> Podcast

in this case?

What if someone makes a podcast later on that includes episodes from other podcasts? In my case this will not happen and even if it is the case, it would be totally okay to consider the same episode as different ones. (In fact considering this issue would lift the 1:n relationship to an n:n relationsship, but the main question how to define those relationships in haskell remains the same).


Solution

  • Cyclic dependencies are actually really easy in Haskell. In a let statement, any binding's definition can refer to any other binding.

    let pc = Podcast "the name" [ep1, ep2]
        ep1 = Episode "first" pc
        ep2 = Episode "second" pc
    

    Laziness will take care of this for you.

    As a general rule though, a DBMS is the best choice for this sort of information.