Мне очень часто хочется моделировать реляционные данные в моих функциональных программах. Например, при разработке веб-сайта я могу иметь следующую структуру данных для хранения информации о моих пользователях:
data User = User
{ name :: String
, birthDate :: Date
}
Затем я хочу сохранить данные о сообщениях, которые публикуют сообщения на моем сайте:
data Message = Message
{ user :: User
, timestamp :: Date
, content :: String
}
С этой структурой данных существует несколько проблем:
- У нас нет возможности различать пользователей с похожими именами и датами рождения.
- Пользовательские данные будут дублироваться при сериализации/десериализации
- Сравнение пользователей требует сравнения их данных, которые могут быть дорогостоящими.
- Обновления полей
User
являются хрупкими - вы можете забыть обновить все вхожденияUser
в своей структуре данных.
Эти проблемы управляются, тогда как наши данные могут быть представлены как дерево. Например, вы можете реорганизовать следующее:
data User = User
{ name :: String
, birthDate :: Date
, messages :: [(String, Date)] -- you get the idea
}
Однако возможно, что ваши данные сформированы как DAG (представьте какое-либо отношение "многие ко многим" ) или даже как общий график (возможно, нет). В этом случае я стараюсь моделировать реляционную базу данных, сохраняя мои данные в Map
s:
newtype Id a = Id Integer
type Table a = Map (Id a) a
Этот вид работ, но небезопасен и уродлив по нескольким причинам:
- Вы просто вызов конструктора
Id
от бессмысленного поиска. - При поиске вы получаете
Maybe a
, но часто база данных структурно гарантирует, что есть значение. - Это неуклюже.
- Трудно обеспечить ссылочную целостность ваших данных.
- Управление индексами (что очень важно для производительности) и обеспечение их целостности еще сложнее и неуклюже.
Существует ли работа по преодолению этих проблем?
Похоже, что Template Haskell может их решить (как это обычно бывает), но я бы не хотел изобретать колесо.