#haskell #functional-programming #adt
Вопрос:
Ну, у меня есть две структуры (два ADT) Cashier
, и Porter
у них есть общее свойство salary
. Это достигается путем реализации класса Worker
.
type Money = Int
type Kg = Int
class Worker a where
salary :: a -> Money
data Cashier = C { salaryC :: Money
, polite :: Bool
}
instance Worker Cashier where
salary = salaryC
data Porter = P { salaryP :: Money
, liftingForce :: Kg
}
instance Worker Porter where
salary = salaryP
Общий интерфейс итерации означает общее действие. Это дает мне возможность не писать две разные вариации какой-либо функции, которая использует общее свойство данных ADTS.
Например:
hasSalary :: (Worker a) => a -> Bool
hasSalary x = (salary x) > 0
Но когда я пытаюсь объявить другой тип, такой как data Stuff = S [Worker]
компилятор, он не позволяет мне сделать это таким образом.
Конечно, это тоже не сработает type Stuff = [(Worker a) => a]
или (Worker a) => type Stuff = [a]
Да, есть тип данных-конструктор:
data (Worker a) => Stuff a = S [a]
и даже версия ГЭДТА:
data Stuff a where
S :: Worker a => [a] -> Stuff [a]
но это не то, чего я действительно хочу. Потому что каждый раз, когда я работаю с Stuff
, будет разница между Stuff Cashier
и Stuff Porter
.
Итак, есть ли какой-либо вариант использования имени класса Worker
в качестве типа поля в структуре, как это было в обычном типе данных ( Int
например)?
Комментарии:
1. Всегда будет существовать разница между
Stuff Cashier
иStuff Porter
, посколькуCashier
иPorter
являются различными и не связанными типами.Worker
это не класс в популярном объектно-ориентированном смысле, если вы этого ожидаете.
Ответ №1:
Не используйте ничего из этого ответа до того, как вы также прочтете эту часть обязательного чтения.
Если я правильно понял вопрос (или, на самом деле, догадался, что это довольно неясно), то, о чем вы просите, — это гетерогенная коллекция: список, который может содержать Cashier
s и Porter
s в некотором произвольном сочетании. В конце концов, это то, что List<Worker>
было бы на таком языке, как Java.
За это,
type Stuff = [(Worker a) => a]
то, что вы пробовали, имеет смысл: a
они должны отображаться только в записях списка, а не в общем типе данных (потому что тогда они должны быть одного типа).
На самом деле, попытка сработала бы, если бы у Хаскелла был экзистенциальный квантор:
type Stuff = [∃ a . Worker a => a]
У Хаскелла этого нет, но можно обернуть экзистенциальное в GADT:
data AWorker where
AWorker :: Worker a => a -> AWorker
type Stuff = [AWorker]
Комментарии:
1. Я бы добавил, что, учитывая класс OP,
[AWorker]
он изоморфен[Money]
(что является предлагаемым решением в сообщении в блоге, на которое вы ссылаетесь).2. Предпринимаются усилия по включению первоклассных
exists.
в GHC: Разрешен экзистенциальный кризис: Вывод типа для первоклассных экзистенциальных типов