Haskell ajoutant un type polymorphe

Oct 25 2020

J'essaye d'ajouter un polymorphe == à un type de données. J'ai ajouté le POLYEQ Var Var aux données Exp et ajouté Eval1 et 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

Mais j'obtiens l'erreur suivante;

Exp.hs: 37:32: erreur:

• 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: erreur:

• 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

Échec, modules chargés: aucun.

Comment puis-je rendre le == polymorphe?


Éditer:

eval1:: Exp -> Bool
eval1 (POLYEQ e19 e20) = eval e19 == eval e20

Le fichier se charge maintenant, mais lorsque j'exécute ti1 = POLYEQ (MyInt 4) (MyInt 7) suivi de eval1 ti1, j'obtiens l'erreur suivante:

: 100: 7: erreur: • Impossible de correspondre au type attendu "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

Réponses

1 JonPurdy Oct 25 2020 at 20:25

Il y a plusieurs problèmes avec votre code. Tout d'abord, pour résoudre votre problème réel, vous devez rendre les champs du POLYEQconstructeur de type Exp, non Var, sinon vous ne pourrez comparer que des variables.

Deuxièmement, vous ne devez pas diviser la evalfonction en plusieurs définitions comme celle-ci. Il semble que vous l'ayez fait pour renvoyer différents types de résultats pour chacun: Intou Bool. Mais l'effet d'écrire votre code de cette façon est que toutes ces fonctions sont partielles : eval0volonté que le travail sur un sous - ensemble d'expressions, et se bloque sur les autres, et vous ne pouvez pas savoir à l' avance quelle fonction de faire appel à un arbitraire Expsans l'examinant en premier.

Une approche conventionnelle simple consiste à ajouter un type de valeurs résultant de l'évaluation, par exemple:

data Val
  = IntVal Int
  | BoolVal Bool

Avec cela, vous pouvez consolider vos fonctions en une seule et étiqueter le résultat de chaque cas avec le Valconstructeur approprié . De plus, vous n'avez pas besoin de nommer toutes vos variables avec des noms distincts, car elles sont locales à chaque cas.

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.

Pour le POLYEQcas, vous devez faire correspondre les résultats de l'évaluation pour affirmer qu'ils sont du même type et comparer en conséquence:

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?

Cependant, cela ne décrit pas comment évaluer les expressions lambda ( L) et les variables. Pour cela, vous devrez ajouter un argument supplémentaire pour evalcontenir la variable d'environnement (par exemple, a [Val], où VZregarde de la tête et VSregarde dans la queue), et un autre constructeur pour Valstocker une valeur de fonction avec son environnement . Cependant, cela dépasse le cadre de votre question actuelle.