Ответ 1
Он определяет новый тип, синтаксис называется обобщенный тип алгебраических данных.
Это более общий, чем обычный синтаксис. Вы можете написать любое стандартное определение типа (ADT) с помощью GADT:
data E a = A a | B Integer
может быть записана как:
data E a where
A :: a -> E a
B :: Integer -> E a
Но вы также можете ограничить то, что находится на правой стороне:
data E a where
A :: a -> E a
B :: Integer -> E a
C :: Bool -> E Bool
что невозможно с нормальным объявлением ADT.
Для получения дополнительной информации, проверьте Haskell wiki или это видео.
Причиной является безопасность типа. ExecutionAST t
должен быть типом операторов, возвращающих t
. Если вы пишете обычный ADT
data ExecutionAST result = Return result
| WriteRegister M_Register Word8
| ReadRegister M_Register
| ReadMemory Word16
| WriteMemory Word16
| ...
то ReadMemory 5
будет полиморфным значением типа ExecutionAST t
вместо мономорфного ExecutionAST Word8
, и это будет проверять тип:
x :: M_Register2
x = ...
a = Bind (ReadMemory 1) (WriteRegister2 x)
Этот оператор должен прочитать память из местоположения 1 и записать в регистр x
. Однако чтение из памяти дает 8-битные слова, а для записи в x
требуются 16-битные слова. Используя GADT, вы можете быть уверены, что это не скомпилируется. Ошибки времени компиляции лучше, чем ошибки времени выполнения.
GADT также включают экзистенциальные типы. Если вы попытались написать bind следующим образом:
data ExecutionAST result = ...
| Bind (ExecutionAST oldres)
(oldres -> ExecutionAST result)
то он не будет компилироваться, так как "oldres" не находится в области видимости, вам нужно написать:
data ExecutionAST result = ...
| forall oldres. Bind (ExecutionAST oldres)
(oldres -> ExecutionAST result)
Если вы запутались, проверьте связанное видео для более простого, родственного примера.