Вы должны обязательно обернуть базовое представление в абстрактный тип данных и предоставить только умные конструкторы для построения и разбора вашей структуры данных кучи.
Изменчивые структуры, как правило, имеют более простые API, чем неизменяемые (поскольку они поддерживают меньшее количество хороших поведений). Чтобы понять, как выглядит разумный API для изменяемого типа контейнера, включая способ абстрагирования представления, возможно, посмотрите на пакет Judy .
В частности,
И API:
new :: JE a => IO (JudyL a)
-- Allocate a new empty JudyL array.
null :: JudyL a -> IO Bool
-- O(1), null. Is the map empty?
size :: JudyL a -> IO Int
-- O(1), size. The number of elements in the map.
lookup :: JE a => Key -> JudyL a -> IO (Maybe a)
-- Lookup a value associated with a key in the JudyL array.
insert :: JE a => Key -> a -> JudyL a -> IO ()
-- Insert a key and value pair into the JudyL array. Any existing key will be overwritten.
delete :: Key -> JudyL a -> IO ()
-- Delete the Index/Value pair from the JudyL array.
Вам нужно будет поддерживать множество одинаковых операций с похожими сигнатурами типов.
Фактическое базовое представление JudyL
определяется как:
newtype JudyL a =
JudyL { unJudyL :: MVar (ForeignPtr JudyL_) }
type JudyL_ = Ptr JudyLArray
data JudyLArray
Обратите внимание, как мы обеспечиваем безопасность потоков, блокируя базовый ресурс (в данном случае указатель C на структуру данных). Отдельно представление является абстрактным и невидимым для пользователя, что делает API простым.