I am currently taking a class in Haskell and am having a bit of trouble understanding how functions are passed as parameters. For this assignment, we were tasked with creating a program that would evaluate expressions. To reduce boiler plating, I wanted to abstract the function by creating a helper function that would take in an operator as an input and return the result
Main Function:
eval :: EDict -> Expr -> Maybe Double
eval _ (Val x) = Just x
eval d (Var i) = find d i
eval d (Add x y) = evalOp d (+) x y
eval d (Mul x y) = evalOp d (*) x y
eval d (Sub x y) = evalOp d (-) x y
Helper Function:
evalOp:: EDict -> ((Num a) => a -> a -> a) -> Expr -> Expr -> Maybe Double
evalOp d op x y =
let r = eval d x
s = eval d y
in case (r, s) of
(Just m, Just n) -> Just (m `op` n)
_ -> Nothing
Other definitions
data Expr
= Val Double
| Add Expr Expr
| Mul Expr Expr
| Sub Expr Expr
| Dvd Expr Expr
| Var Id
| Def Id Expr Expr
deriving (Eq, Show)
type Dict k d = [(k,d)]
define :: Dict k d -> k -> d -> Dict k d
define d s v = (s,v):d
find :: Eq k => Dict k d -> k -> Maybe d
find [] _ = Nothing
find ( (s,v) : ds ) name | name == s = Just v
| otherwise = find ds name
type EDict = Dict String Double
I looked into how +,-, and * are to be passed into other functions and found that these operators are defined by the following definition:
ghci> :t (*)
(*) :: (Num a) => a -> a -> a
However, when I run my code I get the following compilation error:
Illegal polymorphic or qualified type: Num a => a -> a -> a
Perhaps you intended to use RankNTypes or Rank2Types
In the type signature for ‘evalOp’:
evalOp :: EDict
-> ((Num a) => a -> a -> a) -> Expr -> Expr -> Maybe Double
I am not really sure why this is happening as I gave my function the proper parameters as defined by Haskell. Any help would be greatly appreciated as I am still very new to the language.
Right now, your Expr data type is constrained to Double-valued expressions, so there is no need to deal with polymorphism.
evalOp:: EDict -> (Double -> Double -> Double) -> Expr -> Expr -> Maybe Double
evalOp d op x y =
let r = eval d x
s = eval d y
in case (r, s) of
(Just m, Just n) -> Just (m `op` n)
_ -> Nothing
(+) :: Num a => a -> a -> a is a valid argument for evalOp, because its type can be "restricted" to Double -> Double -> Double.
> let f :: Double -> Double -> Double; f = (+)
> f 3 5
8.0
If your expression type were parameterized, then you would put a Num a constraint on your functions (not just on the arguments that involve a, because you want the same a throughout the function).
data Expr a
= Val a
| Add (Expr a) (Expr a)
| Mul (Expr a) (Expr a)
| Sub (Expr a) (Expr a)
| Dvd (Expr a) (Expr a)
| Var Id
| Def Id (Expr a) (Expr a)
deriving (Eq, Show)
type EDict a = Dict String a
evalOp:: Num a => EDict a -> (a -> a -> a) -> Expr a -> Expr a -> Maybe a
evalOp d op x y =
let r = eval d x
s = eval d y
in case (r, s) of
(Just m, Just n) -> Just (m `op` n)
_ -> Nothing
eval :: Num a => EDict a -> Expr a -> Maybe a
eval _ (Val x) = Just x
eval d (Var i) = find d i
eval d (Add x y) = evalOp d (+) x y
eval d (Mul x y) = evalOp d (*) x y
eval d (Sub x y) = evalOp d (-) x y
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With