Haskell dodaje typ polimorficzny

Oct 25 2020

Próbuję dodać polimorficzny == do typu danych. Dodałem POLYEQ Var Var do Data Exp i dodałem Eval1 i 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

Ale pojawia się następujący błąd;

Exp. Hs: 37: 32: błąd:

• 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: błąd:

• 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

Niepowodzenie, załadowano moduły: brak.

Jak mogę ustawić == polimorficzny?


edytować:

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

Plik ładuje się teraz, ale po uruchomieniu ti1 = POLYEQ (MyInt 4) (MyInt 7), a następnie eval1 ti1, pojawia się następujący błąd:

: 100: 7: błąd: • Nie można dopasować oczekiwanego typu „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

Odpowiedzi

1 JonPurdy Oct 25 2020 at 20:25

Istnieje kilka problemów z Twoim kodem. Po pierwsze, aby rozwiązać swój rzeczywisty problem, powinieneś utworzyć pola POLYEQkonstruktora typu Exp, a nie Var, w przeciwnym razie będziesz mógł tylko porównać zmienne.

Po drugie, nie powinieneś dzielić evalfunkcji na wiele takich definicji. Wygląda na to, że zrobiłeś to, aby zwrócić różne typy wyników z każdego z nich: Intlub Bool. Ale efekt pisania kodu w ten sposób jest taki, że wszystkie te funkcje są częściowe : eval0będą działać tylko na podzbiorze wyrażeń i będą się zawieszać na innych, a nie możesz z wyprzedzeniem wiedzieć, którą funkcję wywołać w dowolnej funkcji Expbez najpierw go zbadać.

Prostym konwencjonalnym podejściem jest dodanie typu wartości wynikających z oceny, na przykład:

data Val
  = IntVal Int
  | BoolVal Bool

Dzięki temu możesz skonsolidować swoje funkcje w jedną i oznaczyć wynik każdego przypadku odpowiednim Valkonstruktorem. Ponadto nie musisz nazywać wszystkich swoich zmiennych różnymi nazwami, ponieważ są one lokalne dla każdego przypadku.

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.

W tym POLYEQprzypadku musisz dopasować wyniki oceny, aby potwierdzić, że są tego samego typu, i odpowiednio porównać:

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?

Jednak to nie opisuje, jak oceniać wyrażenia lambda ( L) i zmienne. W tym celu musisz dodać dodatkowy argument evalzawierający zmienną environment (na przykład a [Val], gdzie VZspogląda w górę od głowy i VSpatrzy w górę) oraz inny konstruktor Valdo przechowywania wartości funkcji w swoim środowisku . Jednak to wykracza poza zakres twojego obecnego pytania.