Использование типов данных в Haskell

Я начал этот новый вопрос, поскольку он стал продолжением моего предыдущего вопроса.

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

data A = X | Y | Z
data B = X | Y

я никак не могу как-то представить это как:

data A = C | Z
data B = C

data C = X | Y

если вы видите, что я делаю - я пытаюсь сгруппировать X | Y в один тип данных, который затем может использоваться несколькими другими типами данных. Кажется, я не могу заставить компилятор разрешить это, или если это так, я не могу сопоставлять шаблоны с X или Y, только с C??

Я получаю сообщение об ошибке, что C был объявлен несколько раз.

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

РЕДАКТИРОВАТЬ

Даже если я объявлю длинный путь (как показано ниже), он все равно не скомпилируется и скажет, что X и Y имеют несколько объявлений:

data A = X | Y | Z
data B = X | Y

person Lethi    schedule 09.09.2012    source источник
comment
То, о чем вы просите, будет подтипом A. Он не будет объявлен с ключевым словом data, которое создает новый тип, не пересекающийся с предыдущими существующими типами. Я не думаю, что в Haskell есть такая функция, но я не в курсе всех расширений Haskell.   -  person Gilles 'SO- stop being evil'    schedule 10.09.2012
comment
@Gilles: Нет, в Haskell нет полиморфизма подтипов. Он имеет только параметрический полиморфизм и специальный полиморфизм через классы типов. Самое близкое, что вы можете получить, это экзистенциальный тип, но это почти, но не совсем, совершенно другое.   -  person Antal Spector-Zabusky    schedule 10.09.2012
comment
Я бы назвал это ответом, но потому что это не совсем так. Возможно, вы сможете приблизиться к тому, что хотите, объявив класс типов, а затем операции, которые вам нужны для этих обычных вещей. Это довольно распространенный способ перевернуть вещи, чтобы решить (вариант) этой проблемы.   -  person Kristopher Micinski    schedule 10.09.2012


Ответы (4)


Вы не только не можете сделать это, вы также не можете сделать свой первый вариант, то есть вы не можете иметь два типа в одном и том же модуле, оба из которых имеют конструкторы с именами X и Y.

Если бы вы могли это сделать, каким должен быть тип XC, A или B? Наиболее очевидным ответом будет C, но тогда вы не сможете использовать его в контексте, где требуются A или B (обратите внимание, что в Haskell нет подтипов), так что это противоречит цели всей конструкции.

Лучшее, что вы можете сделать, это обернуть C конструктором A и B, т.е.:

data A = AC C | Z
data B = BC C
data C = X | Y

Затем вы можете обернуть C конструктором AC или BC, чтобы создать значение типа A или B соответственно.

person sepp2k    schedule 09.09.2012
comment
Просто отредактировал, чтобы сказать, что мой первый вариант не работает. Когда вы говорите обернуть, вы буквально имеете в виду включить какой-то другой фрагмент синтаксиса перед C, чтобы отличить его от оригинального C? - person Lethi; 10.09.2012
comment
@ Дэн Да. В моем примере вы должны написать AC X для создания значения типа A и BC X для создания значения типа B. X само по себе не может иметь два разных типа. - person sepp2k; 10.09.2012

Причина, по которой вы не можете этого сделать

data A = X | Y | Z
data B = X | Y

как следует. Скажем, вы пишете код позже:

foo n = (n,X)

который создает пару, состоящую из n в первом слоте и X во втором слоте. Какой тип должен вывести компилятор? Допустимый тип будет

foo :: a -> A -> (a,A)

поскольку X является конструктором типа A, но в равной степени допустимым является

foo :: a -> B -> (a,B)

так как X является конструктором типа B. Если у вас есть два конструктора с одинаковыми именами, вы не можете вывести уникальный тип для функций, которые их используют. Таким образом, вам запрещено давать двум конструкторам в одном модуле одно и то же имя.

person Chris Taylor    schedule 10.09.2012

Вы не можете сделать это:

data A = C | Z
data B = C

data C = X | Y

(Кроме того, если B идентично C, то зачем вообще B?)

Но вы можете сделать что-то вроде этого:

data A = A_Other C | Z
data B = B_Other C

data C = X | Y

Затем вы можете сопоставить шаблон следующим образом:

foo :: A -> String
foo (A_Other X) = "X"
foo (A_Other Y) = "Y"
foo (        Z) = "Z"

bar :: B -> String
bar (B_Other X) = "X"
bar (B_Other Y) = "Y"

foobar :: C -> String
foobar X = "X"
foobar Y = "Y"

Если это имеет смысл...

person MathematicalOrchid    schedule 10.09.2012

Вы не можете делать то, что хотите, потому что вы объявляете несколько конструкторов данных. В

data A = X | Y | Z

На самом деле вы вводите тип A, который имеет 3 конструктора (значения) X, Y и Z. Вот почему ваш первый фрагмент кода не будет компилироваться, он видит одно и то же имя в качестве конструкторов для двух разных типов! Если бы вы могли сделать это, вы должны были бы спросить себя,

X :: A

or

X :: B

что в не объектно-ориентированном контексте пугает! Поэтому вам нужно предоставить разные имена конструкторов для совместного использования этих базовых данных, C.

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

data A = CForA C | Z
data B = CForB C

data C = X | Y
person ScottWest    schedule 10.09.2012