параметры нескольких типов в классах типа haskell - PullRequest
9 голосов
/ 21 ноября 2008

Я пытаюсь создать абстракцию в Haskell98, но не знаю, как это сделать.

Я хочу определить класс для типов, которые могут быть преобразованы в списки.

toList :: a -> [b]

Но я не знаю, как определить класс для этого метода. Я выдвинул следующие три идеи:

class ToList a b where
    toList :: a -> [b]

class ToList a where
    toList :: a -> [b]

class ToList a where
    toList :: a b -> [b]

Первый из них не работает, потому что Haskell98 не поддерживает несколько классов параметров.

Второй не работает, потому что b зависит от a и не может быть реализован для каждого b.

Третий тоже не работает, потому что я не знаю, как создать экземпляр класса с типом, где 'b' не является последним параметром типа.

data HTree a b = Nil | Node a b (HTree a b) (HTree a b)

toList Nil = []
toList Node x y l r = toList l ++ [(x,y)] ++ toList r

или

toList Nil = []
toList Node x y l r = toList l ++ [x] ++ toList r

Как бы я сделал что-то подобное?

Ответы [ 3 ]

9 голосов
/ 23 ноября 2008

См. Также Data.Foldable в стандартной библиотеке, которая обеспечивает функцию toList для любого экземпляра Foldable. Foldable требуется немного искушенности, чтобы создать экземпляр, но это было бы хорошей практикой. В качестве бонуса ваш тип HTree практически совпадает с примером в документации.

Кроме того, я рекомендую изменить HTree на:

data HTree a = Nil | Node a (HTree a) (HTree a)

И затем использовать HTree (a,b) вместо HTree a b. Эта однопараметрическая версия будет легче компоноваться со стандартными типами и экземплярами, и она будет ближе к сути происходящего, поскольку она зависит от обоих параметров одинаково. Это также Functor, и определение такого экземпляра сделает этот тип действительно удобным для работы.

4 голосов
/ 22 ноября 2008

Я бы порекомендовал Классы типов не так полезны, как кажется на первый взгляд - если предполагаемый класс имеет только один интерфейсный метод, рассмотрите возможность объявления типа функции вместо этого. Я тоже пришел из ОО и обнаружил, что потратил слишком много времени, пытаясь сделать «класс» значащим то, что, как я думал, означало, когда на самом деле мне следовало использовать «данные».

Гораздо проще просто написать свою функцию toList и затем "поднять" ее, чтобы она работала с вашей структурой данных. Фактически, получившее признание И еще одно руководство по Haskell проходит обширное упражнение, показывающее, как это делается, и использует в качестве примера двоичное дерево. Самое замечательное в выполнении лифта заключается в том, что он различает то, что важно - структуру типа данных, а не реализацию toList ', поэтому после выполнения лифта для выполнения' в порядке обхода типа данных 'вы можете использовать лифт для делать что угодно - перечислять, печатать, что угодно. Поддержка toList не является важной частью структуры данных, поэтому она не должна быть в объявлении класса - важная часть - как пройти через структуру данных.

0 голосов
/ 22 ноября 2008

Возможно, вы захотите выбрать последний вариант для класса ToList и создать (HTree a) экземпляр ToList. Тогда toList имеет тип (HTree a b) -> [b], не например (HTree a b) -> [(a,b)]. Я предполагаю, что вы думаете, что «ключ» и b как тип «значение».

class ToList a where
    toList :: a b -> [b]

data HTree a b = Nil | Node a b (HTree a b) (HTree a b)

instance ToList (HTree a) where
    toList Nil = []
    toList (Node x y l r) = toList l ++ [y] ++ toList r

test = toList (Node "a" 1 (Node "b" 2 Nil Nil) Nil)
-- test == [2,1]
...