Складной экземпляр для Trie-Set

У меня есть структура данных типа Set, реализованная как Trie, с таким определением:

import qualified Data.Map as M
import Data.Foldable (Foldable, foldr)
import Prelude hiding (foldr)
import Data.Maybe (fromMaybe)

data Trie a = Trie { endHere :: Bool 
                   , getTrie :: M.Map a (Trie a)
                   } deriving (Eq)

И операция вставки, которая выглядит так:

insert :: (Ord a, Foldable f) => f a -> Trie a -> Trie a
insert = foldr f (\(Trie _ m) -> Trie True m) where
  f e a = overMap (M.alter (Just . a . fromMaybe (Trie False M.empty)) e)

overMap :: Ord b => (M.Map a (Trie a) -> M.Map b (Trie b)) -> Trie a -> Trie b
overMap f (Trie e m) = Trie e (f m)

И я могу получить разновидность foldr, которая выглядит так:

foldrTrie :: ([a] -> b -> b) -> b -> Trie a -> b
foldrTrie f i (Trie a m) = M.foldrWithKey ff s m where
  s    = if a then f [] i else i
  ff k = flip (foldrTrie $ f . (k :))

Но я не могу определить экземпляр Foldable для Trie. foldrTrie, кажется, имеет все необходимые функции, но я просто не могу понять типы.

Вот пример поведения foldr, которое я ищу:

fromList :: (Ord a, Foldable f, Foldable g) => f (g a) -> Trie a
fromList = foldr insert (Trie False M.empty)

toList :: (Ord a) => Trie a -> [[a]]
toList = foldr (:) [] -- replace foldr here with foldrTrie and you'll get the 
                      -- desired behaviour

toList (fromList ["abc", "def"]) -- ["abc","def"]

Я не могу управлять подписью типа для Foldable:

instance Foldable Trie a where

Я попытался сделать свой Trie параметром второго типа:

data Trie a (f a) = Trie { endHere :: Bool
                         , getTrie :: M.Map a (Trie a (f a))
                         } deriving (Eq)

Чтобы я мог сделать что-то вроде этого:

instance Foldable Trie a f where
  foldr f i (Trie a m) = M.foldrWithKey ff s m where
    s    = if a then f [] i else i
    ff k = flip (foldrTrie $ f . (k :))

но я не мог понять типы.

Более общий способ постановки вопроса может быть таким: если бы у меня была структура данных, которая могла бы хранить только списки, смог бы я определить foldr в этой структуре данных, чтобы он обрабатывал списки, которые он хранится как каждый элемент? Как будет выглядеть тип этой структуры данных?


person oisdk    schedule 02.11.2015    source источник
comment
instance Foldable Trie where foldr f = foldrTrie $ flip $ foldr f. Я не вижу причин, по которым Trie может хранить только складные вещи.   -  person user2407038    schedule 02.11.2015
comment
Этот Trie предназначен как структура данных для хранения последовательностей (или, в более общем смысле, Foldables) из Ord элементов. Вещи, хранящиеся в Trie должны быть Foldable, потому что это единственный способ вставить что-то в Trie. Я бы хотел снова получить доступ к объектам в Trie, используя foldr, сохраняя их структуру, подобную последовательности.   -  person oisdk    schedule 02.11.2015
comment
Я уверен, что есть много способов представить последовательность, но я не понимаю, как Foldable это делает. Используя только Foldable интерфейс для Trie, я не вижу возможности написать желаемую функцию, потому что Foldable этого просто не делает. foldrTrie является более общим, чем foldr : (a -> b -> b) -> b -> Trie a -> b.   -  person user2407038    schedule 02.11.2015
comment
Я предполагаю, что я спрашиваю, если бы у меня была структура данных, которая могла бы хранить только (скажем) списки, мог бы я реализовать Foldable в этой структуре данных? Я могу управлять механикой (думаю); но меня сдерживают типы. Как будет выглядеть подпись типа для этой структуры?   -  person oisdk    schedule 02.11.2015


Ответы (1)


Вероятно, это не то, что вы хотите сделать, но вы можете обернуть общую структуру данных в GADT, который позволяет хранить только списки на листах. Простой пример использования деревьев вместо попыток для простоты: предположим, что общая структура данных Tree, и мы хотим сделать LTree, который допускает только деревья списков:

{-# LANGUAGE GADTs #-}

import Prelude hiding (foldr)
import Data.Foldable
import Data.Tree

foldrForListTrees :: ([a] -> b -> b) -> b -> Tree [a] -> b
foldrForListTrees = error "This is the one you are supposed to be able to write"

data LTree a where
    MkLTree :: Tree [a] -> LTree [a]

instance Foldable LTree where
    foldr f ys0 (MkLTree xs) = foldrForListTrees f ys0 xs
person Cactus    schedule 02.11.2015
comment
В этом случае также можно было бы определить Trie как такой GADT напрямую, за счет добавления [] вокруг его параметра типа повсюду. - person Ørjan Johansen; 02.11.2015
comment
Я думаю, что это действительно хороший вариант. В любом случае я намеревался еще немного обобщить Trie (т. Е. Использовать его в качестве структуры сопоставления, заменив endHere типами, отличными от Bool), так что, возможно, я мог бы иметь STrie GADT (с [] вокруг его параметра типа повсюду) , а затем еще один Trie, который мог бы реализовать Foldable другим способом. Спасибо! - person oisdk; 02.11.2015