Haskell添加多态类型 [英] Haskell adding a polymorphic type
问题描述
我正在尝试将多态==添加到数据类型.我已将POLYEQ Var Var添加到数据Exp中,并添加了Eval1和Eval2:
I am trying to add a polymorphic == to a data type. I have added the POLYEQ Var Var to data Exp and added Eval1 and Eval2:
{-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-}
data Exp = V Var
| B Bool
| L Exp
| A Exp Exp
| MyInt Int
| And Exp Exp
| Or Exp Exp
| Not Exp
| Mult Exp Exp
| UnaryNeg Exp
| LEQ Exp Exp
| LESST Exp Exp
| Add Exp Exp
| POLYEQ Var Var
data Var = VZ |VS Var
eval:: Exp -> Int
eval (MyInt e4) = e4
eval (UnaryNeg e10) = - (eval e10)
eval (Mult e11 e12) = eval e11 * eval e12
eval (Add e1 e2) = eval e1 + eval e2
eval0:: Exp -> Bool
eval0 (B e5) = e5
eval0 (Not e3) = not (eval0 e3)
eval0 (And e6 e7) = (eval0 e6) && (eval0 e7)
eval0 (Or e8 e9) = (eval0 e8) || (eval0 e9)
eval0 (LEQ e13 e14) = eval e13 <= eval e14
eval0 (LESST e15 e16) = eval e15 < eval e16
eval2:: Exp -> Var
eval2 (V e22) = e22
eval1:: a -> Bool
eval1 (POLYEQ e19 e20) = eval2 e19 == eval2 e20
但是我收到跟随错误;
But I get the followng error;
Exp.hs:37:32:错误:
Exp.hs:37:32: error:
• Couldn't match expected type ‘Exp’ with actual type ‘Var’
• In the first argument of ‘eval2’, namely ‘e19’
In the first argument of ‘(==)’, namely ‘eval2 e19’
In the expression: eval2 e19 == eval2 e20
Exp.hs:37:45:错误:
Exp.hs:37:45: error:
• Couldn't match expected type ‘Exp’ with actual type ‘Var’
• In the first argument of ‘eval2’, namely ‘e20’
In the second argument of ‘(==)’, namely ‘eval2 e20’
In the expression: eval2 e19 == eval2 e20
失败,已加载模块:无.
Failed, modules loaded: none.
如何使==多态?
eval1:: Exp -> Bool
eval1 (POLYEQ e19 e20) = eval e19 == eval e20
文件现在加载,但是当我运行ti1 = POLYEQ(MyInt 4)(MyInt 7)时接下来是eval1 ti1我得到以下错误:
The file loads now, but when I run ti1 = POLYEQ (MyInt 4) (MyInt 7) followed by eval1 ti1 I get the following error:
:100:7:错误:•无法匹配预期的类型"Exp"
:100:7: error: • Couldn't match expected type ‘Exp’
with actual type ‘Exp -> Exp -> Exp’
• Probable cause: ‘POLYEQ’ is applied to too few arguments
In the first argument of ‘eval1’, namely ‘POLYEQ’
In the expression: eval1 POLYEQ
In an equation for ‘it’: it = eval1 POLYEQ
推荐答案
您的代码有几个问题.首先,要解决您的实际问题,应将 POLYEQ
构造函数的字段设置为 Exp
类型,而不是 Var
类型,否则您将只能能够比较变量.
There are several issues with your code. First, to solve your actual problem, you should make the fields of the POLYEQ
constructor of type Exp
, not Var
, otherwise you will only be able to compare variables.
第二,您不应将 eval
函数拆分为多个这样的定义.看来您这样做是为了从每一个返回不同类型的结果: Int
或 Bool
.但是以这种方式编写代码的结果是所有这些函数都是 partial : eval0
将仅可用于表达式的子集,并且会崩溃,而您无法提前知道不调用哪个 Exp
哪个函数.
Second, you should not split the eval
function into multiple definitions like this. It looks like you’ve done so in order to return different types of results from each one: Int
or Bool
. But the effect of writing your code this way is that all of these functions are partial: eval0
will only work on a subset of expressions, and will crash on others, and you can’t know ahead of time which function to call on an arbitrary Exp
without examining it first.
一种简单的常规方法是添加评估产生的值类型,例如:
A simple conventional approach is to add a type of values resulting from evaluation, for example:
data Val
= IntVal Int
| BoolVal Bool
这样,您可以将函数合并为一个,并使用适当的 Val
构造函数标记每种情况的结果.另外,您不必使用不同的名称来命名所有变量,因为它们在每种情况下都是本地的.
With this, you can consolidate your functions into one, and tag the result of each case with the appropriate Val
constructor. In addition, you don’t need to name all of your variables with distinct names, since they’re local to each case.
eval :: Exp -> Val
-- Evaluation of literals: tag the value with its type.
eval (MyInt i) = IntVal i
eval (B b) = BoolVal b
-- Evaluation of integer operations: match on ‘IntVal’.
-- This will raise an error if the expression did not return an integer.
eval (UnaryNeg e) = let
IntVal i = eval e -- Unwrap result, asserting that it’s an integer.
in IntVal (- i) -- Rewrap in ‘IntVal’ after applying negation.
eval (Mult e1 e2) = let
IntVal i1 = eval e1
IntVal i2 = eval e2
in IntVal (i1 * i2)
-- Instead of just crashing, you may use explicit
-- pattern matching and handle the type error:
eval (Add e1 e2) = case (eval e1, eval e2) of
(IntVal i1, IntVal i2) -> IntVal (i1 + i2)
_ -> ... -- Decide what to do in the error case.
-- Fill in the remaining cases for each ‘Exp’ constructor.
对于 POLYEQ
案例,您需要匹配评估结果以断言它们是同一类型,并进行相应比较:
For the POLYEQ
case, you need to match on the results of evaluation to assert that they’re the same type, and compare accordingly:
eval (POLYEQ e1 e2) = case (eval e1, eval e2) of
(IntVal i1, IntVal i2) -> BoolVal (i1 == i2)
(BoolVal b1, BoolVal b2) -> BoolVal (b1 == b2)
_ -> ... -- What do you want to do in this case?
-- Return ‘BoolVal False’, raise an error, or something else?
但是,这并未说明如何计算lambda表达式( L
)和变量.为此,您需要在 eval
中添加一个包含变量环境的附加参数(例如, [Val]
,其中 VZ
从头向上查找,在 VS
在末尾查找),以及另一个用于 Val
的构造函数,用于将函数值与其环境一起存储.但是,这超出了您当前问题的范围.
However, this doesn’t describe how to evaluate lambda expressions (L
) and variables. For that, you’ll need to add an additional argument to eval
containing the variable environment (for example, a [Val]
, where VZ
looks up from the head, and VS
looks up within the tail), and another constructor for Val
to store a function value with its environment. However, this is beyond the scope of your current question.
这篇关于Haskell添加多态类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!