Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • SEARCH
  • Home
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 7501947
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: May 29, 20262026-05-29T20:42:09+00:00 2026-05-29T20:42:09+00:00

I find it very common to want to model relational data in my functional

  • 0

I find it very common to want to model relational data in my functional programs. For example, when developing a web-site I may want to have the following data structure to store info about my users:

data User = User 
  { name :: String
  , birthDate :: Date
  }

Next, I want to store data about the messages users post on my site:

data Message = Message
  { user :: User
  , timestamp :: Date
  , content :: String
  }

There are multiple problems associated with this data structure:

  • We don’t have any way of distinguishing users with similar names and birth dates.
  • The user data will be duplicated on serialisation/deserialisation
  • Comparing the users requires comparing their data which may be a costly operation.
  • Updates to the fields of User are fragile — you can forget to update all the occurences of User in your data structure.

These problems are manageble while our data can be represented as a tree. For example, you can refactor like this:

data User = User
  { name :: String
  , birthDate :: Date
  , messages :: [(String, Date)] -- you get the idea
  }

However, it is possible to have your data shaped as a DAG (imagine any many-to-many relation), or even as a general graph (OK, maybe not). In this case, I tend to simulate the relational database by storing my data in Maps:

newtype Id a = Id Integer
type Table a = Map (Id a) a

This kind of works, but is unsafe and ugly for multiple reasons:

  • You are just an Id constructor call away from nonsensical lookups.
  • On lookup you get Maybe a, but often the database structurally ensures that there is a value.
  • It is clumsy.
  • It is hard to ensure referential integrity of your data.
  • Managing indices (which are very much necessary for performance) and ensuring their integrity is even harder and clumsier.

Is there existing work on overcoming these problems?

It looks like Template Haskell could solve them (as it usually does), but I would like not to reinvent the wheel.

  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-05-29T20:42:10+00:00Added an answer on May 29, 2026 at 8:42 pm

    The ixset library (or ixset-typed, a more type-safe version) will help you with this. It’s the library that backs the relational part of acid-state, which also handles versioned serialization of your data and/or concurrency guarantees, in case you need it.

    The Happstack Book has an IxSet tutorial.


    The thing about ixset is that it manages "keys" for your data entries automatically.

    For your example, one would create one-to-many relationships for your data types like this:

    data User =
      User
      { name :: String
      , birthDate :: Date
      } deriving (Ord, Typeable)
    
    data Message =
      Message
      { user :: User
      , timestamp :: Date
      , content :: String
      } deriving (Ord, Typeable)
    
    instance Indexable Message where
      empty = ixSet [ ixGen (Proxy :: Proxy User) ]
    

    You can then find the message of a particular user. If you have built up an IxSet like this:

    user1 = User "John Doe" undefined
    user2 = User "John Smith" undefined
    
    messageSet =
      foldr insert empty
      [ Message user1 undefined "bla"
      , Message user2 undefined "blu"
      ]
    

    … you can then find messages by user1 with:

    user1Messages = toList $ messageSet @= user1
    

    If you need to find the user of a message, just use the user function like normal. This models a one-to-many relationship.

    Now, for many-to-many relations, with a situation like this:

    data User =
      User
      { name :: String
      , birthDate :: Date
      , messages :: [Message]
      } deriving (Ord, Typeable)
    
    data Message =
      Message
      { users :: [User]
      , timestamp :: Date
      , content :: String
      } deriving (Ord, Typeable)
    

    … you create an index with ixFun, which can be used with lists of indexes. Like so:

    instance Indexable Message where
      empty = ixSet [ ixFun users ]
    
    instance Indexable User where
      empty = ixSet [ ixFun messages ]
    

    To find all the messages by an user, you still use the same function:

    user1Messages = toList $ messageSet @= user1
    

    Additionally, provided that you have an index of users:

    userSet =
      foldr insert empty
      [ User "John Doe" undefined [ messageFoo, messageBar ]
      , User "John Smith" undefined [ messageBar ]
      ]
    

    … you can find all the users for a message:

    messageFooUsers = toList $ userSet @= messageFoo
    

    If you don’t want to have to update the users of a message or the messages of a user when adding a new user/message, you should instead create an intermediary data type that models the relation between users and messages, just like in SQL (and remove the users and messages fields):

    data UserMessage = UserMessage { umUser :: User, umMessage :: Message } 
    
    instance Indexable UserMessage where
      empty = ixSet [ ixGen (Proxy :: Proxy User), ixGen (Proxy :: Proxy Message) ]
    

    Creating a set of these relations would then let you query for users by messages and messages for users without having to update anything.

    The library has a very simple interface considering what it does!

    EDIT: Regarding your "costly data that needs to be compared": ixset only compares the fields that you specify in your index (so to find all the messages by a user in the first example, it compares "the whole user").

    You regulate which parts of the indexed field it compares by altering the Ord instance. So, if comparing users is costly for you, you can add an userId field and modify the instance Ord User to only compare this field, for example.

    This can also be used to solve the chicken-and-egg problem: what if you have an id, but neither a User, nor a Message?

    You could then simply create an explicit index for the id, find the user by that id (with userSet @= (12423 :: Id)) and then do the search.

    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

I find it very frustrating doing web development with Maven & Jetty using Eclipse,
I parse data using JSON in javascript, I find this is very convenient. But
Very basic question - but I couldn't find an answer. I have got a
I have two very large strings and I am trying to find out their
I have a ASP.Net web page with a grid view. I want to filter
I have the very common problem of creating an index for an in-disk array
This seems like a very simple and a very common problem. The simplest example
Ok, this is an very common scenario in web dev, with a very common
This seems to be a common question, but I've yet to find a very
I think this is a very common situation, but I can't find how everybody

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.