Evaluating a string of operations but code does not work - haskell

I need to write a code that evaluates a string of operations and outputs the resulting integer of that string. I wrote something but it's not working and would like some help. I need to use fold since it is easier but I'm sure what's wrong. This is on Haskell and using Emacs.
evalExpr :: String -> Int
evalExpr xs = foldl 0 xs where
f v x | x == "+" = (+) v
| x == "-" = (-) v
| x == " " = 0
| otherwise = read v :: Int
For example:
evalExpr "2+4+5-8"
the output should be: 3
evalExpr ""
the output should be: 0
This is because it should read the string left to right.

You can do as #5ndG suggested. However, to evaluate a string of operations, using parsec is a better way. Here is an example for your case:
module EvalExpr where
-- You need parsec to do parsing work, and the following are just example
-- modes for your simple case.
import Text.Parsec
import Text.Parsec.Char
import Text.Parsec.String
-- A data structure for your simple arithmetic expresssion
data Expr = Lit Int
| Plus Expr Expr
| Minus Expr Expr
deriving Show
-- Evaluate an Expr to an integer number
eval :: Expr -> Int
eval (Lit n) = n
eval (Plus e1 e2) = eval e1 + eval e2
eval (Minus e1 e2) = eval e1 - eval e2
-- The following do the parsing work
-- Parser for an integer number
int :: Parser Expr
int = Lit . read <$> (many1 digit <* spaces) -- A number may be followed by spaces
-- Parser for operators "Plus" and "Minus"
plus, minus :: Parser (Expr -> Expr -> Expr)
plus = Plus <$ char '+' <* spaces
minus = Minus <$ char '-' <* spaces
-- Parser for Expr
expr :: Parser Expr
expr = chainl int (plus <|> minus) (Lit 0)
-- Evalute string to an integer
evalExpr :: String -> Int
evalExpr s = case parse expr "" s of
Left err -> error $ show err
Right e -> eval e
The above is just an simple example of using parsec. If your actual case is more complex, you'll need more work to do. So learning to use parsec is necessary. The intro_to_parsing is a good start. Also in the package description are there some learning resources.
By the way, Text.Parsec.Expr in parsec can parse an expression more conveniently, but above all, you need to know the basic of parsec.
Happy learning!

You're not far off something that works on your examples. Try this:
evalExpr :: String -> Int
evalExpr xs = foldl f (0 +) xs 0
f :: (Int -> Int) -> Char -> (Int -> Int)
f v ch | ch == '+' = (v 0 +)
| ch == '-' = (v 0 -)
| ch == ' ' = v
| otherwise = (v (read [ch] :: Int) +)
So the main difference to yours is that the accumulator in the fold is a function that takes in one Int and produces one Int, instead of just being an Int.

Related

Chaining unary operators with megaparsec

I have written a parser using megaparsec for a very simple language consisting of integer literals and two unary operators "a" and "b":
data ValueExpr = OpA ValueExpr
| OpB ValueExpr
| Integer Integer
valueExpr :: Parser ValueExpr
valueExpr = makeExprParser valueTerm valueOperatorTable
valueTerm :: Parser ValueExpr
valueTerm = parenthesised valueExpr
<|> Integer <$> integerLiteral
integerLiteral :: Parser Integer
integerLiteral = -- omitted
valueOperatorTable :: [[Operator Parser ValueExpr]]
valueOperatorTable = [[unaryOp "a" AOp,
unaryOp "b" BOp]]
parenthesised :: Parser a -> Parser a
parenthesised = between (char '(') (char ')')
unaryOp :: Text -> (a -> a) -> Operator Parser a
unaryOp name f = Prefix (f <$ symbol name)
binaryOp :: Text -> (a -> a -> a) -> Operator Parser a
binaryOp name f = InfixL (f <$ symbol name)
However, it seems that this doesn't allow me to "chain" unary operators, i.e. when trying to parse "ab1", I'm met with "unexpected 'b'". Why is that?
This is briefly mentioned in the documentation for makeExprParser:
Unary operators of the same precedence can only occur once (i.e., --2 is not allowed if - is prefix negate). If you need to parse several prefix or postfix operators in a row, ... you can use this approach:
manyUnaryOp = foldr1 (.) <$> some singleUnaryOp
This is not done by default because in some cases allowing repeating prefix or postfix operators is not desirable.
In your specific example, something like the following ought to work:
valueOperatorTable :: [[Operator Parser ValueExpr]]
valueOperatorTable = [[Prefix unaryOps]]
unaryOps :: Parser (ValueExpr -> ValueExpr)
unaryOps = foldr1 (.) <$> some (OpA <$ symbol "a" <|> OpB <$ symbol "b")

main function in haskell instead of ghci

hi i'm new in using haskell.
i'm actually using ghci to compile this code:
module Expr where
import Control.Applicative
import System.Environment
data Expr = Add Expr Expr
| Sub Expr Expr
| Mul Expr Expr
| Lit Integer
eval :: Expr -> Integer
eval e = case e of
Add a b -> eval a + eval b
Sub a b -> eval a - eval b
Mul a b -> eval a * eval b
Lit n -> n
-- Nouveau datatype nécessaire pour les instances
data Parser r = Parser {parse :: String -> Maybe (r, String)}
-- Instances
instance Functor Parser where
fmap f (Parser p) = Parser $ \s -> case p s of
Just (a, s') -> Just (f a, s')
Nothing -> Nothing
instance Applicative Parser where
pure x = Parser $ \s -> Just (x, s)
Parser p1 <*> pp2 = Parser $ \s -> case p1 s of
Just (f, s') -> case parse pp2 s' of
Just (a, s'') -> Just (f a, s'')
Nothing -> Nothing
Nothing -> Nothing
instance Alternative Parser where
empty = Parser $ const Nothing
(Parser p1) <|> pp2 = Parser $ \s -> p1 s <|> parse pp2 s
-- Le reste est identique
runParser :: Parser a -> String -> Maybe a
runParser (Parser p) s = case p s of
Just (r, "") -> Just r
_ -> Nothing
check :: (Char -> Bool) -> Parser Char
check f = Parser $ \s -> case s of
(x:xs) | f x -> Just (x, xs)
_ -> Nothing
char :: Char -> Parser Char
char c = check (== c)
oneOf :: [Char] -> Parser Char
oneOf cs = check (\c -> elem c cs)
number :: Parser Integer
number = read <$> some digit
where digit = oneOf "0123456789"
expr :: Parser Expr
expr = add_sub
where
add_sub = binOp Add '+' mul <|> binOp Sub '-' mul <|> mul
mul = binOp Mul '*' factor <|> factor
factor = parens <|> lit
lit = Lit <$> number
parens = char '(' *> expr <* char ')'
binOp c o p = c <$> p <*> (char o *> p)
evalExpr :: String -> Maybe Integer
evalExpr s = (fmap eval) $ runParser expr $ concat $ words s
what i'm trying to do is to create a Main to compile and run this code by myself and i'm struggling to do so.
so far i manage to understand and i would like to have something that is structurated like this:
module Main where
import System.Environment
import Expr
main :: IO ()
main = do
args <- getArgs
case args of
[] -> putStrLn "No argument"
[s]-> evalExpr args
i want to know if i can use ghci to generate a main or if there is another solution to create one
You already know that you need to create a constant called main whose type is IO ().
Let's start at your compilation errors. These are coming up because you have indented the contents of you main action less than the do keyword. Because Haskell is whitespace-sensitive, this is confusing the compiler. So your example above should actually look like this:
main :: IO ()
main = do args <- getArgs
case args of [] -> putStrLn "No argument"
[s] -> evalExpr args
I can't tell you whether this is actually what you want in your main action because I don't know what you want it to do, but it will probably look similar to what you're running in GHCi.
For example, if your GHCi session looks like this:
Prelude> let x = foo bar
Prelude> print x
the equivalent main would look like this:
main :: IO ()
main = do let x = foo bar
print x
Assuming you've created a project using cabal or stack, then your Expr module should be in src/Expr.hs and your Main module should be in src/Main.hs or app/Main.hs. The code you have looks at first glance like it should compile and run fine.
You can then either run a REPL instance (cabal repl or stack repl) in your project directory and call the main function from there or you can run it as an executable (cabal run or stack run) and pass in your arguments from the command-line.
If you haven't created a project using either of the tools I mentioned above, then this documentation should prove useful:
Cabal documentation
Stack documentation
Edit:
I had neglected to look at the code in detail before/try to compile & run it.
Your main function has some issues with the pattern match line of:
[s] -> evalExpr args
Since your evalExpr function takes a String you need to pass it something instead of args, which is of type [String].
Also, the result of evalExpr is of type Maybe Integer and needs to be of type IO () to end the case statement properly.
If you replace it with this:
_ -> putStrLn . show $ evalExpr (unwords args)
then running stack run 1 + 1 from the command-line will output Just 2.
To get this to run as a stand-alone executable, you'll have to run stack install which will create an executable on your system that you can run. On mine it outputs to ~/.local/bin/Expr-exe which I can then call:
paul#Pauls-MacBook-Pro Expr % ~/.local/bin/Expr-exe 1 + 1
Just 2
(side note: stack install is apparently a short-cut for stack build --copy-bins)

How to reduce code duplication when dealing with recursive sum types

I am currently working on a simple interpreter for a programming language and I have a data type like this:
data Expr
= Variable String
| Number Int
| Add [Expr]
| Sub Expr Expr
And I have many functions that do simple things like:
-- Substitute a value for a variable
substituteName :: String -> Int -> Expr -> Expr
substituteName name newValue = go
where
go (Variable x)
| x == name = Number newValue
go (Add xs) =
Add $ map go xs
go (Sub x y) =
Sub (go x) (go y)
go other = other
-- Replace subtraction with a constant with addition by a negative number
replaceSubWithAdd :: Expr -> Expr
replaceSubWithAdd = go
where
go (Sub x (Number y)) =
Add [go x, Number (-y)]
go (Add xs) =
Add $ map go xs
go (Sub x y) =
Sub (go x) (go y)
go other = other
But in each of these functions, I have to repeat the part that calls the code recursively with just a small change to one part of the function. Is there any existing way to do this more generically? I would rather not have to copy and paste this part:
go (Add xs) =
Add $ map go xs
go (Sub x y) =
Sub (go x) (go y)
go other = other
And just change a single case each time because it seems inefficient to duplicate code like this.
The only solution I could come up with is to have a function that calls a function first on the whole data structure and then recursively on the result like this:
recurseAfter :: (Expr -> Expr) -> Expr -> Expr
recurseAfter f x =
case f x of
Add xs ->
Add $ map (recurseAfter f) xs
Sub x y ->
Sub (recurseAfter f x) (recurseAfter f y)
other -> other
substituteName :: String -> Int -> Expr -> Expr
substituteName name newValue =
recurseAfter $ \case
Variable x
| x == name -> Number newValue
other -> other
replaceSubWithAdd :: Expr -> Expr
replaceSubWithAdd =
recurseAfter $ \case
Sub x (Number y) ->
Add [x, Number (-y)]
other -> other
But I feel like there should probably be a simpler way to do this already. Am I missing something?
Congratulations, you just rediscovered anamorphisms!
Here's your code, rephrased so that it works with the recursion-schemes package. Alas, it's not shorter, since we need some boilerplate to make the machinery work. (There might be some automagic way to avoid the boilerplate, e.g. using generics. I simply do not know.)
Below, your recurseAfter is replaced with the standard ana.
We first define your recursive type, as well as the functor it is the fixed point of.
{-# LANGUAGE DeriveFunctor, TypeFamilies, LambdaCase #-}
{-# OPTIONS -Wall #-}
module AnaExpr where
import Data.Functor.Foldable
data Expr
= Variable String
| Number Int
| Add [Expr]
| Sub Expr Expr
deriving (Show)
data ExprF a
= VariableF String
| NumberF Int
| AddF [a]
| SubF a a
deriving (Functor)
Then we connect the two with a few instances so that we can unfold Expr into the isomorphic ExprF Expr, and fold it back.
type instance Base Expr = ExprF
instance Recursive Expr where
project (Variable s) = VariableF s
project (Number i) = NumberF i
project (Add es) = AddF es
project (Sub e1 e2) = SubF e1 e2
instance Corecursive Expr where
embed (VariableF s) = Variable s
embed (NumberF i) = Number i
embed (AddF es) = Add es
embed (SubF e1 e2) = Sub e1 e2
Finally, we adapt your original code, and add a couple of tests.
substituteName :: String -> Int -> Expr -> Expr
substituteName name newValue = ana $ \case
Variable x | x == name -> NumberF newValue
other -> project other
testSub :: Expr
testSub = substituteName "x" 42 (Add [Add [Variable "x"], Number 0])
replaceSubWithAdd :: Expr -> Expr
replaceSubWithAdd = ana $ \case
Sub x (Number y) -> AddF [x, Number (-y)]
other -> project other
testReplace :: Expr
testReplace = replaceSubWithAdd
(Add [Sub (Add [Variable "x", Sub (Variable "y") (Number 34)]) (Number 10), Number 4])
An alternative could be to define ExprF a only, and then derive type Expr = Fix ExprF. This saves some of the boilerplate above (e.g. the two instances), at the cost of having to use Fix (VariableF ...) instead of Variable ..., as well as the analogous for the other constructors.
One could further alleviate that using pattern synonyms (at the cost of a little more boilerplate, though).
Update: I finally found the automagic tool, using template Haskell. This makes the whole code reasonably short. Note that the ExprF functor and the two instances above still exist under the hood, and we still have to use them. We only save the hassle of having to define them manually, but that alone saves a lot of effort.
{-# LANGUAGE DeriveFunctor, DeriveTraversable, TypeFamilies, LambdaCase, TemplateHaskell #-}
{-# OPTIONS -Wall #-}
module AnaExpr where
import Data.Functor.Foldable
import Data.Functor.Foldable.TH
data Expr
= Variable String
| Number Int
| Add [Expr]
| Sub Expr Expr
deriving (Show)
makeBaseFunctor ''Expr
substituteName :: String -> Int -> Expr -> Expr
substituteName name newValue = ana $ \case
Variable x | x == name -> NumberF newValue
other -> project other
testSub :: Expr
testSub = substituteName "x" 42 (Add [Add [Variable "x"], Number 0])
replaceSubWithAdd :: Expr -> Expr
replaceSubWithAdd = ana $ \case
Sub x (Number y) -> AddF [x, Number (-y)]
other -> project other
testReplace :: Expr
testReplace = replaceSubWithAdd
(Add [Sub (Add [Variable "x", Sub (Variable "y") (Number 34)]) (Number 10), Number 4])
As an alternative approach, this is also a typical use case for the uniplate package. It can use Data.Data generics rather than Template Haskell to generate the boilerplate, so if you derive Data instances for your Expr:
import Data.Data
data Expr
= Variable String
| Number Int
| Add [Expr]
| Sub Expr Expr
deriving (Show, Data)
then the transform function from Data.Generics.Uniplate.Data applies a function recursively to each nested Expr:
import Data.Generics.Uniplate.Data
substituteName :: String -> Int -> Expr -> Expr
substituteName name newValue = transform f
where f (Variable x) | x == name = Number newValue
f other = other
replaceSubWithAdd :: Expr -> Expr
replaceSubWithAdd = transform f
where f (Sub x (Number y)) = Add [x, Number (-y)]
f other = other
Note that in replaceSubWithAdd in particular, the function f is written to perform a non-recursive substitution; transform makes it recursive in x :: Expr, so it's doing the same magic to the helper function as ana does in #chi's answer:
> substituteName "x" 42 (Add [Add [Variable "x"], Number 0])
Add [Add [Number 42],Number 0]
> replaceSubWithAdd (Add [Sub (Add [Variable "x",
Sub (Variable "y") (Number 34)]) (Number 10), Number 4])
Add [Add [Add [Variable "x",Add [Variable "y",Number (-34)]],Number (-10)],Number 4]
>
This is no shorter than #chi's Template Haskell solution. One potential advantage is that uniplate provides some additional functions that may be helpful. For example, if you use descend in place of transform, it transforms only the immediate children which can give you control over where the recursion happens, or you can use rewrite to re-transform the result of transformations until you reach a fixed point. One potential disadvantage is that "anamorphism" sounds way cooler than "uniplate".
Full program:
{-# LANGUAGE DeriveDataTypeable #-}
import Data.Data -- in base
import Data.Generics.Uniplate.Data -- package uniplate
data Expr
= Variable String
| Number Int
| Add [Expr]
| Sub Expr Expr
deriving (Show, Data)
substituteName :: String -> Int -> Expr -> Expr
substituteName name newValue = transform f
where f (Variable x) | x == name = Number newValue
f other = other
replaceSubWithAdd :: Expr -> Expr
replaceSubWithAdd = transform f
where f (Sub x (Number y)) = Add [x, Number (-y)]
f other = other
replaceSubWithAdd1 :: Expr -> Expr
replaceSubWithAdd1 = descend f
where f (Sub x (Number y)) = Add [x, Number (-y)]
f other = other
main = do
print $ substituteName "x" 42 (Add [Add [Variable "x"], Number 0])
print $ replaceSubWithAdd e
print $ replaceSubWithAdd1 e
where e = Add [Sub (Add [Variable "x", Sub (Variable "y") (Number 34)])
(Number 10), Number 4]

How to evaluate expressions in Haskell

I understand how to create and evaluate a simple data-type Expr. For example like this:
data Expr = Lit Int | Add Expr Expr | Sub Expr Expr | [...]
eval :: Expr -> Int
eval (Lit x) = x
eval (Add x y) = eval x + eval y
eval (Sub x y) = eval x - eval y
So here is my question: How can I add Variables to this Expr type, which should be evaluated for its assigned value? It should look like this:
data Expr = Var Char | Lit Int | Add Expr Expr [...]
type Assignment = Char -> Int
eval :: Expr -> Assignment -> Int
How do I have to do my eval function now for (Var Char) and (Add Expr Expr)? I think I figured out the easiest, how to do it for Lit already.
eval (Lit x) _ = x
For (Var Char) I tried a lot, but I cant get an Int out of an Assignment.. Thought It would work like this:
eval (Var x) (varname number) = number
Well if you model your enviroment as
type Env = Char -> Int
Then all you have is
eval (Var c) env = env c
But this isn't really "correct". For one, what happens with unbound variables? So perhaps a more accurate type is
type Env = Char -> Maybe Int
emptyEnv = const Nothing
And now we can see whether a variable is unbound
eval (Var c) env = maybe handleUnboundCase id (env c)
And now we can use handleUnboundCase to do something like assign a default value, blow up the program, or make monkeys climb out of your ears.
The final question to ask is "how are variables bound?". If you where looking for a "let" statement like we have in Haskell, then we can use a trick known as HOAS (higher order abstract syntax).
data Exp = ... | Let Exp (Exp -> Exp)
The HOAS bit is that (Exp -> Exp). Essentially we use Haskell's name-binding to implement our languages. Now to evaluate a let expression we do
eval (Let val body) = body val
This let's us dodge Var and Assignment by relying on Haskell to resolve the variable name.
An example let statement in this style might be
Let 1 $ \x -> x + x
-- let x = 1 in x + x
The biggest downside here is that modelling mutability is a royal pain, but this was already the case when relying on the Assignment type vs a concrete map.
You need to apply your Assignment function to the variable name to get the Int:
eval (Var x) f = f x
This works because f :: Char -> Int and x:: Char, so you can just do f x to get an Int.
Pleasingly this will work across a collection of variable names.
Example
ass :: Assignment
ass 'a' = 1
ass 'b' = 2
meaning that
eval ((Add (Var 'a') (Var 'b')) ass
= eval (Var 'a') ass + eval (Var 'b') ass
= ass 'a' + ass 'b'
= 1 + 2
= 3
Pass the assignment functions through to other calls of eval
You need to keep passing the assignment function around until you get integers:
eval (Add x y) f = eval x f + eval y f
Different order?
If you're allowed to change the types, it seems more logical to me to put the assignment function first and the data second:
eval :: Assignment -> Expr -> Int
eval f (Var x) = f x
eval f (Add x y) = eval f x + eval f y
...but I guess you can think of it as a constant expression with varying variables (feels imperative)rather than a constant set of values across a range of expressions (feels like referential transparency).
I would recommend using Map from Data.Map instead. You could implement it something like
import Data.Map (Map)
import qualified Data.Map as M -- A lot of conflicts with Prelude
-- Used to map operations through Maybe
import Control.Monad (liftM2)
data Expr
= Var Char
| Lit Int
| Add Expr Expr
| Sub Expr Expr
| Mul Expr Expr
deriving (Eq, Show, Read)
type Assignment = Map Char Int
eval :: Expr -> Assignment -> Maybe Int
eval (Lit x) _ = Just x
eval (Add x y) vars = liftM2 (+) (eval x vars) (eval y vars)
eval (Sub x y) vars = liftM2 (-) (eval x vars) (eval y vars)
eval (Mul x y) vars = liftM2 (*) (eval x vars) (eval y vars)
eval (Var x) vars = M.lookup x vars
But this looks clunky, and we'd have to keep using liftM2 op every time we added an operation. Let's clean it up a bit with some helpers
(|+|), (|-|), (|*|) :: (Monad m, Num a) => m a -> m a -> m a
(|+|) = liftM2 (+)
(|-|) = liftM2 (-)
(|*|) = liftM2 (*)
infixl 6 |+|, |-|
infixl 7 |*|
eval :: Expr -> Assignment -> Maybe Int
eval (Lit x) _ = return x -- Use generic return instead of explicit Just
eval (Add x y) vars = eval x vars |+| eval y vars
eval (Sub x y) vars = eval x vars |-| eval y vars
eval (Mul x y) vars = eval x vars |*| eval y vars
eval (Var x) vars = M.lookup x vars
That's a better, but we still have to pass around the vars everywhere, this is ugly to me. Instead, we can use the ReaderT monad from the mtl package. The ReaderT monad (and the non-transformer Reader) is a very simple monad, it exposes a function ask that returns the value you pass in when it's run, where all you can do is "read" this value, and is usually used for running an application with static configuration. In this case, our "config" is an Assignment.
This is where the liftM2 operators really come in handy
-- This is a long type signature, let's make an alias
type ExprM a = ReaderT Assignment Maybe a
-- Eval still has the same signature
eval :: Expr -> Assignment -> Maybe Int
eval expr vars = runReaderT (evalM expr) vars
-- evalM is essentially our old eval function
evalM :: Expr -> ExprM Int
evalM (Lit x) = return x
evalM (Add x y) = evalM x |+| evalM y
evalM (Sub x y) = evalM x |-| evalM y
evalM (Mul x y) = evalM x |*| evalM y
evalM (Var x) = do
vars <- ask -- Get the static "configuration" that is our list of vars
lift $ M.lookup x vars
-- or just
-- evalM (Var x) = ask >>= lift . M.lookup x
The only thing that we really changed was that we have to do a bit extra whenever we encounter a Var x, and we removed the vars parameter. I think this makes evalM very elegant, since we only access the Assignment when we need it, and we don't even have to worry about failure, it's completely taken care of by the Monad instance for Maybe. There isn't a single line of error handling logic in this entire algorithm, yet it will gracefully return Nothing if a variable name is not present in the Assignment.
Also, consider if later you wanted to switch to Doubles and add division, but you also want to return an error code so you can determine if there was a divide by 0 error or a lookup error. Instead of Maybe Double, you could use Either ErrorCode Double where
data ErrorCode
= VarUndefinedError
| DivideByZeroError
deriving (Eq, Show, Read)
Then you could write this module as
data Expr
= Var Char
| Lit Double
| Add Expr Expr
| Sub Expr Expr
| Mul Expr Expr
| Div Expr Expr
deriving (Eq, Show, Read)
type Assignment = Map Char Double
data ErrorCode
= VarUndefinedError
| DivideByZeroError
deriving (Eq, Show, Read)
type ExprM a = ReaderT Assignment (Either ErrorCode) a
eval :: Expr -> Assignment -> Either ErrorCode Double
eval expr vars = runReaderT (evalM expr) vars
throw :: ErrorCode -> ExprM a
throw = lift . Left
evalM :: Expr -> ExprM Double
evalM (Lit x) = return x
evalM (Add x y) = evalM x |+| evalM y
evalM (Sub x y) = evalM x |-| evalM y
evalM (Mul x y) = evalM x |*| evalM y
evalM (Div x y) = do
x' <- evalM x
y' <- evalM y
if y' == 0
then throw DivideByZeroError
else return $ x' / y'
evalM (Var x) = do
vars <- ask
maybe (throw VarUndefinedError) return $ M.lookup x vars
Now we do have explicit error handling, but it isn't bad, and we've been able to use maybe to avoid explicitly matching on Just and Nothing.
This is a lot more information than you really need to solve this problem, I just wanted to present an alternative solution that uses the monadic properties of Maybe and Either to provide easy error handling and use ReaderT to clean up that noise of passing an Assignment argument around everywhere.

Undefined Variable, Haskell

I've got a quick question. Haskell is throwing the 57 - Undefined variable "f" error at me and I've no idea why. I'd appreciate it if you could have a look at this.
Code:
eval :: Expr -> Environment -> Float
eval expr env = eval' expr
where
eval' :: Expr-> Float
eval' (Num num) = num
eval' (App app exprs) = foldl1 (f) (map eval' exprs) -- **Line 57**
eval' (Id id) = 5
where
f = getFunctionForApp app -- **f is here**
getFunctionForApp :: String -> (Float->Float->Float)
getFunctionForApp "+" = (+)
getFunctionForApp "-" = (-)
getFunctionForApp "*" = (*)
getFunctionForApp "/" = (/)
getIdVal :: String -> Environment -> Float
getIdVal id ((curId, val):envrs)
|curId == id = val
| otherwise = getIdVal id envrs
Type definition:
data Expr = Num Float | Id String | App String [ Expr ]
deriving (Eq, Ord, Show)
type Environment = [ ( String, Float ) ]
The where block applies only to the case directly before it, not to all cases of the eval' function. So f is defined (but not used) in eval' (Id id) = 5, but it's not in scope in line 57. To fix this you need to move the where block directly after line 57.
Exactly what sepp2k said. In this case, though, I'd prefer simply swapping lines 57 and 58, so the where is attached to the right equation without splitting the equations for eval', that's more readable.
Or don't use f at all, make it
eval' (App app exprs) = foldl1 (getFunctionOrApp app) (map eval' exprs)
eval' (Id id) = 5
getFunctionOrApp :: String -> (Float -> Float -> Float)
getFunctionOrApp "+" = ...
Moving getFunctionOrApp (and getIdVal) to the same-level where as eval', it may even be reasonable to define them at the top level.

Resources