Haskell adicionando um tipo polimórfico

Oct 25 2020

Estou tentando adicionar um polimórfico == a um tipo de dados. Eu adicionei o POLYEQ Var Var aos dados Exp e adicionei Eval1 e 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

Mas recebo o seguinte erro;

Exp.hs: 37: 32: erro:

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

• 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

Falha, módulos carregados: nenhum.

Como posso tornar == polimórfico?


editar:

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

O arquivo carrega agora, mas quando executo ti1 = POLYEQ (MyInt 4) (MyInt 7) seguido por eval1 ti1, obtenho o seguinte erro:

: 100: 7: erro: • Não foi possível corresponder ao tipo esperado '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

Respostas

1 JonPurdy Oct 25 2020 at 20:25

Existem vários problemas com o seu código. Primeiro, para resolver seu problema real, você deve fazer os campos do POLYEQconstrutor do tipo Exp, não Var, caso contrário você só conseguirá comparar variáveis.

Em segundo lugar, você não deve dividir a evalfunção em várias definições como esta. Parece que você fez isso para retornar diferentes tipos de resultados de cada um: Intou Bool. Mas o efeito de escrever o código desta maneira é que todas estas funções estão parcial : eval0vontade única obra em um subconjunto de expressões, e irá falhar sobre os outros, e você não pode saber de antemão qual função a ser chamada em um arbitrária Exp, sem examinando-o primeiro.

Uma abordagem convencional simples é adicionar um tipo de valores resultantes da avaliação, por exemplo:

data Val
  = IntVal Int
  | BoolVal Bool

Com isso, você pode consolidar suas funções em uma e marcar o resultado de cada caso com o Valconstrutor apropriado . Além disso, você não precisa nomear todas as suas variáveis ​​com nomes distintos, pois eles são locais para cada caso.

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.

Para o POLYEQcaso, você precisa combinar os resultados da avaliação para afirmar que eles são do mesmo tipo e comparar de acordo:

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?

No entanto, isso não descreve como avaliar expressões lambda ( L) e variáveis. Para isso, você precisará adicionar um argumento adicional para evalconter o ambiente da variável (por exemplo, a [Val], onde VZolha para cima a partir da cabeça e VSolha para cima na cauda) e outro construtor para Valarmazenar um valor de função com seu ambiente . No entanto, isso está além do escopo de sua pergunta atual.