Странность структур данных Haskell

#haskell #data-structures #types

#haskell #структуры данных #типы

Вопрос:

Я пытался написать небольшой файл, чтобы опробовать структуру данных, подобную пакету. Пока что мой код выглядит следующим образом:

 data Fruit = Apple | Banana | Pear deriving (Eq, Show)
data Bag a = EmptyBag | Contents [(a, Integer)]

emptyBag :: Bag a
emptyBag = EmptyBag

unwrap :: [a] -> a
unwrap [x] = x

isObject theObject (obj, inte) = theObject == obj

count :: Bag a -> a -> Integer
count (Contents [xs]) theObject = snd (unwrap (filter (isObject theObject) [xs]))
count EmptyBag _ = 0
  

Но когда я пытаюсь запустить его, я получаю ошибку
Не удалось вывести (Eq a) из контекста ()
возникает из-за использования ‘isObject’ в ….

Принимая во внимание, что когда я извлекаю функцию count и вызываю snd(unwrap(filter (isObject Banana) [(Apple, 1),(Banana, 2)])), она с радостью возвращает 2.

Любые подсказки о том, почему это так, или советы по написанию такого рода структуры данных были бы высоко оценены.

Комментарии:

1. В EmptyBag конструкторе нет необходимости; используйте Contents [] вместо этого.

2. count также произойдет сбой при попытке сделать что-то вроде, count (Contents [(Banana,2),(Apple,3)]) Apple поскольку [xs] совпадения в списках происходят только с одним элементом!

Ответ №1:

(==) может использоваться только в контексте, который включает Eq , но при объявлении count вы не включили этот контекст. Если я правильно читаю, это было бы

 count :: Eq a => Bag a -> a -> Integer
  

Если вы объявляете, count не включая тип, вы можете запросить ghci предполагаемый тип; или просто запросить предполагаемый тип snd (unwrap (filter (isObject Banana) [(Apple,1),(Banana,2)]))

Комментарии:

1. Ваше решение правильное, но я не совсем понимаю почему; Я предположил, что, поскольку функция isObject работает для сравнений, она не перестанет работать в этом контексте — что именно меняет добавление Eq a => в программе?

2. Eq a сообщает вашему компилятору, что theObject является членом Eq класса typeclass, а именно, что вы можете проверить, равен ли он чему-то еще того же типа. Существуют определенные типы данных (функции — лучший пример), которые не являются членами Eq класса typeclass. (Например, ( 1) и (( 2).(-1)) невозможно проверить на равенство, хотя почти во всех случаях они вернут один и тот же результат.)

3. Тайно функция класса типов, такая как (==) , просматривается в таблице, содержащей информацию о типе; Eq a => объявляет тип этой таблицы, который передается в качестве дополнительного аргумента за кулисами. (Если вы посмотрите на вывод ядра GHC, вы увидите, что фактические таблицы передаются по кругу.) С помощью isObject вы позволяете GHC самому определять тип, и он это сделал, включая Eq a ограничение; но когда вы указываете тип count самостоятельно, вы не можете оставить подобные вещи без внимания (по крайней мере, без некоторых довольно сложных правил). Все или ничего.

4. Удаление явной подписи типа на count также устраняет проблему. Затем вы могли бы использовать ghci, чтобы увидеть, какой тип выводится.