Есть ли какой-либо способ использовать имя класса в качестве имени обычного типа данных?

#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: Разрешен экзистенциальный кризис: Вывод типа для первоклассных экзистенциальных типов