什么数据…在Haskell意味着什么?

我在omegagb的devlog上看到了这个片段:

data ExecutionAST result where Return :: result -> ExecutionAST result Bind :: (ExecutionAST oldres) -> (oldres -> ExecutionAST result) -> ExecutionAST result WriteRegister :: M_Register -> Word8 -> ExecutionAST () ReadRegister :: M_Register -> ExecutionAST Word8 WriteRegister2 :: M_Register2 -> Word16 -> ExecutionAST () ReadRegister2 :: M_Register2 -> ExecutionAST Word16 WriteMemory :: Word16 -> Word8 -> ExecutionAST () ReadMemory :: Word16 -> ExecutionAST Word8 

data ... where是什么意思? 我以为关键字data是用来定义一个新的types。

它定义了一个新的types,语法被称为广义代数数据types 。

它比正常的语法更普遍。 您可以使用GADT编写任何正常的types定义(ADT):

 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或此video 。


原因是types安全。 ExecutionAST t应该是返回t的语句types。 如果你写一个正常的ADT

 data ExecutionAST result = Return result | WriteRegister M_Register Word8 | ReadRegister M_Register | ReadMemory Word16 | WriteMemory Word16 | ... 

那么ReadMemory 5将是typesExecutionAST t一个多态值,而不是单形的ExecutionAST Word8 ,并且这将会inputcheck:

 x :: M_Register2 x = ... a = Bind (ReadMemory 1) (WriteRegister2 x) 

该语句应该从位置1读取内存并写入寄存器x 。 但是,从内存中读取给出8位字,写入x需要16位字。 通过使用GADT,你可以肯定这不会被编译。 编译时错误比运行时错误好。

GADT还包括存在types 。 如果你试图用这种方式写入绑定:

 data ExecutionAST result = ... | Bind (ExecutionAST oldres) (oldres -> ExecutionAST result) 

那么它将不会编译,因为“oldres”不在范围内,你必须写:

 data ExecutionAST result = ... | forall oldres. Bind (ExecutionAST oldres) (oldres -> ExecutionAST result) 

如果您感到困惑,请查看链接的video以获取更简单的相关示例。

请注意,也可以把类限制:

 data E a where A :: Eq b => b -> E b