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

时间:2019-08-02 11:37:07

标签: haskell functional-programming

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

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)?任何见解都会受到赞赏。

2 个答案:

答案 0 :(得分:3)

function sendOrder() { var ui = SpreadsheetApp.getUi(); var response = ui.alert('Are you sure you want to send the order?', ui.ButtonSet.YES_NO); var deliveryDate = ui.prompt('Delivery date:'); // Process the user's response. if (response == ui.Button.YES) { var s = SpreadsheetApp.getActive().getSheetById('1pON34oXVhlpC8goyBxfu6-Gw92tgQBUVUpskZUtgp4E'); var ss = SpreadsheetApp.getActiveSpreadsheet(); var range = ss.getActiveSheet().getDataRange(); var range = s.getRange('A1:C102'); var to = "example@example.com"; var body = ''; var htmlTable = SheetConverter.convertRange2html(range); var body = "Here is the table:<br/><br/>" + htmlTable + "<br/><br/>The end." MailApp.sendEmail(to, 'Subject', body, { htmlBody: body }); }; SpreadsheetApp.getUi().alert('Yeah! Your order has been sent :)'); } 是可能具有许多个可能功能的类型。例如,一个可能不是您认为的“ Exp a -> a函数”的有效函数就是

eval

函数的 image 是它可以返回的一组值。此示例说明,尽管返回类型为foo :: Exp a -> a foo (Plus _ _) = 3 foo (Const x) = x foo (Not _) = True foo (And _ _) = True foo (Greater _ _) = True ,但您期望的函数fooeval具有不同的图像。

您实质上是在问forall a. a类型的函数的所有可能图像的 union 是什么。这在很大程度上取决于Exp a -> a的实际定义。按照当前的定义,这将是ExpIntBool的并集。

但是,

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

更改eval的定义以包含此类构造函数将增加可以传递给类型Exp的函数的值集,从而增加图像中可能出现的值集这样的功能。

答案 1 :(得分:0)

要清楚,给定您在上面的Exp的定义,答案是“是”:任何类型签名为eval :: Exp a -> a的函数只能返回Int类型的(定义)值,Bool或其他带有Show实例的类型。因为可以为任何类型(*类型)指定一个Show实例,所以从技术上讲,eval可以返回任何类型,但是在具有固定{{ 1}}个实例,Show将仅限于从这组类型中返回值。

您可以看到,这必须满足以下条件。假设eval为某个表达式eval e返回了某个固定类型t的值。根据{{​​1}}的类型签名,这意味着e必须具有类型eval。但是,数据类型声明是“封闭的”,这意味着e声明中给出的构造函数集是详尽的,它们代表构造Exp t类型的(定义)值的唯一方法。从一组构造函数中可以明显看出,类型data Exp a的唯一可能值是出现在构造函数签名最右边的那些值:Exp aExp a和{{1} },约束为Exp Int。因此,这是Exp Bool唯一可能的类型,这意味着Exp a必须是Show ae或其他满足约束{{1}的其他t }。

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

a

但是,如果您问这个问题的原因是要确定您是否曾经遇到过表达式Show a可能意外地具有evalShow以外的其他类型,或某些您需要处理的stupid :: Exp (Int -> Int) stupid = eval undefined ,然后否。 GADT的形式限制了签名eval e中可能的类型Int