13

Как Haskell решает проблему «нормализованной неизменной структуры данных»?Нормализованная и неизменная модель данных

Например, давайте рассмотрим структуру данных, представляющие бывшие подружек/бойфренд:

data Man = Man {name ::String, exes::[Woman]} 

data Woman = Woman {name :: String, exes::[Man]} 

Что происходит, если женщина меняет свое имя, и она была с 13 людей? Тогда весь 13 человек должен быть «обновлен» (в смысле Хаскелла) тоже? Чтобы избежать этих «обновлений», необходима какая-то нормализация.

Это очень простой пример, но представьте себе модель с 20 сущностями и произвольные отношения между ними, что делать тогда?

Каков рекомендуемый способ представления сложных нормализованных данных на неизменяемом языке?

Например, решение Scala можно найти here (см. Код ниже), и оно использует ссылки. Что делать в Хаскелле?

class RefTo[V](val target: ModelRO[V], val updated: V => AnyRef) { 
    def apply() = target() 
} 

Интересно, если более общие решения, как один из приведенных выше (в Scala) не работают в Haskell или они не нужны? Если они не работают, то почему бы и нет? Я попытался найти библиотеки, которые делают это в Haskell, но они, похоже, не существуют.

Другими словами, если я хочу моделировать нормализованную базу данных SQL в Haskell (например, для использования с acid-state), существует ли общий способ описания внешних ключей? Под общим я имею в виду, а не вручную кодирование идентификаторов, как предложено chepner в комментариях ниже.

EDIT:

Еще другими словами, есть библиотека (для Haskell или Scala), который реализует SQL/реляционных баз данных в памяти (возможно также с помощью Event Sourcing для персистенции) таким образом, что база данных является неизменным и большинство операций SQL (запрос/соединение/вставка/удаление/и т. д.) реализованы и безопасны по типу? Если такой библиотеки нет, почему бы и нет? Кажется, это довольно хорошая идея. Как мне создать такую ​​библиотеку?

EDIT 2:

Некоторые ссылки по теме:

+6

Если у вас есть нормализованные данные, разве у вас нет данных Man = Man {name :: String, exes :: [WomanID]} ', где' womanID' был индексом в истории структуры данных «Женщина» (что-то вроде «Map WomanID Woman»? Если вы измените имя значения «Женщина», это не повлияет на какое-либо значение «Человек», ссылающееся на него, вам нужно только обновить одно значение в «Карта». – chepner

+0

Правильно, данный пример не нормирован. Вопрос в том, существует ли общее решение для создания нормализованных структур данных (что-то, что заботится об обработке идентификаторов и т. д.)? Я имею в виду, что делают Haskellers, когда они хотят создать нормализованные данные? Всегда ли они передают код идентификаторам? Или есть еще какое-то общее решение для этой проблемы? Что вы предложили, это пример ручной кодировки идентификаторов, но это может быть автоматизировано, я думаю. Интересно, какое общее решение для создания и резол Эти идентификаторы. – jhegedus

+2

@jhegedus Вопрос в его нынешнем виде немного широк - это действительно зависит от ситуации. Если вы постоянно обновляете мужчин и женщин, вы можете выполнить вычисления в государственной монаде (государство - таблица/карта мужчин/женщин). Если вы ищете функциональный подход к более общим структурам графов, проверьте ['fgl'] (https://hackage.haskell.org/package/fgl). Что касается идентификаторов: есть ситуации, когда вы можете [связать узел] (https://wiki.haskell.org/Tying_the_Knot) (иногда даже используя «карту»), но в целом вам может потребоваться передать идентификаторы кода , – Alec

ответ

9

Проблема заключается в том, что вы сохраняете данные и отношения одного типа. Чтобы нормализовать, вам нужно отделить. Реляционные базы данных 101.

newtype Id a = Id Int -- Type-safe ID. 
data Person = Person { id :: Id Person, name :: String } 
data Ex = Ex { personId :: Id Person, exId :: Id Person } 

Теперь, если человек меняет свое имя, только один Person значения влияет. Записи Ex не заботятся о именах людей.

+1

Интересный момент! На самом деле это очень много. Однако то, что я получил, - как упоминалось в вопросе, - это «не кодирование идентификаторов, как предложено chepner в комментариях ниже». Некоторая библиотека, которая делает то, что вы предложили, но убирает весь шаблон и добавляет поддержку запросов, присоединяется и еще много чего.В основном база данных SQL, но вместо использования SQL в качестве языка запросов он использует Haskell для описания вещей, которые могут быть описаны с помощью SQL. Тип безопасный и неизменный. – jhegedus

+0

@jhegedus здесь вещь. Вы не можете абстрагировать внешний ключ; это часть вашей семантики домена, т. е. она отвечает на вопрос «Что делает человека в отношениях однозначно _that_ person?» Никакая библиотека не может решить это для вас. – Yawar

+0

Таким образом, в основном 'Ex' соответствует таблице базы данных SQL, которая должна быть явно указана. Я получаю это, но, возможно, SQL может быть заменен Haskell, полностью безопасным, неизменным. Или нет ? Интересно, почему это не было сделано? (Не привязка к базе данных, а фактическая база данных в памяти, написанная в Haskell). Я спрашиваю об этом, потому что друг пишет (успешно) приложение в Scala и делает именно это, потому что 1) нет дублирования данных (база данных памяти), 2) типа безопасности 3) Scala более выразительна, чем SQL. Поэтому, если это стоит сделать в Scala, почему нет Haskell lib. существует для этого? Я задаюсь вопросом – jhegedus

0

Проект M63 подходит довольно close к тому, что я искал. Это написано в Haskell.

Более легкое решение Haskell изложено в сообщении Gabriel Gonzalez «A very general API for relational joins».

+0

Можете ли вы привести пример того, как применить проект M63 специально к модели, о которой вы просили? Кроме того, ссылка с текстом «закрыть», похоже, на сообщение в вашем личном почтовом ящике Gmail. Техника присоединения Габриэля Гонсалеса довольно чертовская, хотя - но она до сих пор не абстрагирует идентификацию ID – Yawar

+0

Спасибо, я исправил ссылку. – jhegedus

+0

Я не знаю, как использовать M36, основываясь на описании, однако, похоже, это то, что я искал. Неизменяемая реляционная база данных. Кажется, на данный момент нет эквивалента Scala (по крайней мере, не публичного). Или, может быть, я просто не знаю об этом? Было бы интересно узнать, есть ли что-то вроде M36 для Scala. – jhegedus