繁体   English   中英

Haskell:了解广义代数数据类型

[英]Haskell: Understanding generalised algebraic data types

我有以下代码,概述了布尔和算术表达式的语言:

data Exp a where
     Plus :: Exp Int -> Exp Int -> Exp Int
     Const :: (Show a) => a -> Exp a 
     Not :: Exp Bool -> Exp Bool
     And :: Exp Bool -> Exp Bool -> Exp Bool
     Greater :: Exp Int -> Exp Int -> Exp Bool

上述语言的评估函数具有以下类型:

eval :: Exp a -> a

我试图了解eval函数可以返回的可能类型是什么。 上面的代码使用GADT,这些GADT支持根据构造函数的类型签名定义Exp a类型。 构造函数的返回类型并不总是Exp a。 我相信eval可以返回的类型包括Int,Bool或实现Show的任何类型的值。 然而,有可能是eval返回除了这三个我前面提到的任何其他类型的(因为函数的返回类型是a )? 任何见解都表示赞赏。

Exp a -> a是可以具有许多可能函数的类型。 例如,一个有效的功能可能不是您认为的“ eval功能,

foo :: Exp a -> a
foo (Plus _ _) = 3
foo (Const x) = x
foo (Not _) = True
foo (And _ _) = True
foo (Greater _ _) = True

函数的图像是它可以返回的一组值。 此示例说明,尽管对所有forall a. a具有相同的返回类型,但您期望的函数fooeval具有不同的图像forall a. a forall a. a

本质上,您在问的是类型为Exp a -> a的功能的所有可能图像的并集是什么。 这在很大程度上取决于Exp的实际定义。 按照当前的定义,这将是IntBoolShow a => a

但是, Exp类型构造函数能够定义无人居住的类型。 即使您没有定义可以创建该类型的值的构造函数,也存在类型Exp (Int -> Int) 由于您无法为任何潜在的eval函数提供类型为Exp (Int -> Int)的值,因此它也不会影响任何此类函数的图像。

更改Exp的定义以包含此类构造函数将增加可以传递给类型为Exp a -> a的函数的值的集合,从而增加在此类函数的图像中可能出现的值的集合。

需要明确的是,给定上面的Exp定义,答案是“是”:任何类型签名为eval :: Exp a -> a函数只能返回IntBool类型或其他类型的(已定义)值Show实例。 因为可以给任何类型( *类型)一个Show实例,所以从技术上讲,它意味着eval可以返回任何类型,但是在具有一组固定Show实例的特定程序中, eval将仅限于从这组Show实例中返回值。类型。

您可以看到,这必须是真的,如下所示。 假设eval e为某些表达式e返回了某个固定类型t的值。 通过eval的类型签名,这意味着e必须具有类型Exp t 但是,数据类型声明是“封闭的”,这意味着在data Exp a声明中给出的构造函数集是穷举性的,它们代表构造Exp a类型的(定义)值的唯一方法。 从一组构造函数中可以明显看出,类型为Exp a的唯一可能值是出现在构造函数签名最右边的那些值: Exp IntExp BoolExp a并带有约束Show a 因此,只有这些类型可能e这意味着t必须IntBool ,或一些其他的a满足约束Show a

一如既往地对Haskell类型进行推理,在考虑未定义/底值时,我们必须格外小心。 如果您认为“返回一个未定义的值”是有意义的,那么,实际上, eval可以“返回”任何类型的一个未定义的值,即使没有Show实例也是如此。 例如,以下将进行类型检查:

stupid :: Exp (Int -> Int)
stupid = eval undefined

但是,如果您要问这个问题的原因是要确定您是否曾经处于过表达eval e可能意外地具有IntBool或某些Show a => a以外的类型的位置,那么您将不得不处理,然后没有。 该GADT的形式放置在可能的类型限制a在签名eval :: Exp a -> a

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM