Escolar Documentos
Profissional Documentos
Cultura Documentos
In this exercise you are required to write an interpreter for a simple firstorder functional programming language called FUN, similar in style to a (very)
stripped-down version of Haskell. Rather than work with source code (characters in a text file, for example) the programs you will be manipulating will
be represented by a Haskell data type. A FUN program consists of a sequence
of top-level function definitions, each of which associates a function name (a
String) with a definition. A definition has two components: the names of the
functions arguments (a list of Strings) and the right-hand side, which is an
expression. For example, the function
f x y z = e
in a Haskell-like syntax would be represented as follows:
( "f", ( [ "x", "y", "z" ], rhs ) )
where rhs is the representation of the expression e above. This may refer to
arguments "x", "y" and "z" by name, just as in Haskell. The arity of a function
is the number of arguments it expects. For example, the arity of "f" here is 3.
FUN supports only integer base types, so every expression evaluation generates an integer result. An expression in FUN is one of the following:
1. A constant, i.e. an integer
2. An argument identifier, i.e. a string
3. A conditional, like Haskells if...then...else...
4. A binary operator application
5. A user-defined function application
There are four familiar built-in operators: +, *, ==, >, represented by constructors of an enumerated data type (see below). Each of these operators has
exactly two arguments, which are expressions. In a well-formed program each
user-defined function application has exactly the right number of argument expressions (corresponding to its arity). With the exception of the final question,
you may assume that all programs you will be manipulating are well-formed in
this sense. The following Haskell types can be used to represent FUN programs:
type Identifier = String
type FunctionName = String
data BinOp = EQU | GRT | ADD | MUL
deriving (Eq,Ord,Show)
data Expr = Val Int | Id Identifier | If Expr Expr Expr |
OpApp BinOp Expr Expr | FunApp FunctionName [ Expr ]
2
-- Factorial
-- Haskell-like equivalent:
-fact :: Function
fact
= ( "fact", ( [ "x" ],
( If ( OpApp
( Val 1
( OpApp
)
)
)
)
A program is represented by a list of top-level function definitions, each like the
above, and a top-level expression, analogous to an expression you might type at
3
the GHCi prompt. Here are two examples that use the max2 and fact functions
(note the Program type synonym above):
eg1, eg2 :: Program
-- When evaluated eg1 should give 15 and eg2 should give 120
-eg1
= ( [ max2 ], FunApp "max2" [ Val 12, Val 15 ] )
eg2
= ( [ max2, fact ], FunApp "fact" [ FunApp "max2" [ Val 5, Val 3 ] ] )
These, and some additional examples, are provided in the template that accompanies this exercise.
Spend a moment to ensure that you understand fully these examples.
Program execution
Execution of a FUN program is performed relative to an environment and a list
of top-level function definitions, such as those above. An environment associates
an identifier with an integer value and will be represented in the interpreter by
a list of (Identifier,Int) pairs:
type Environment
= [ ( Identifier, Int ) ]
What to do
1. Define a function search :: Eq a => a -> [ ( a, b ) ] -> b that
looks up the value associated with a key, in a list of (key,value) pairs. Note
that the key can be any type that is a member of Eq, whilst the value can
have any type. For example, search "x" [("a",198),("x",2),("y",12),("x",8)]
should return 2. You may assume that there is always at least one binding
for the given item in the list. If there is more than one binding you should
return the leftmost one, as in the example. Note that this function can be
used to look up both an identifier in an environment and a function name
in a list of function definitions.
[2 marks]
2. Define a function arity :: Function -> ( FunctionName, Int ) that,
given a user-defined function, returns both the name of the function and its
arity, in the form of a pair. For example, arity ("f",(["x","y","z"],Val
1)) should return ("f",3).
[1 mark]
4
iii. Return the result of evaluating rhs in the environment e extended with additional bindings that associate the argument
names (as) with their values (vs) pairwise. Remember to add
the new bindings to the front of e.
[5 marks]
Note that the list of function definitions remains unchanged throughout,
but you need to carry it round to implement the rules for eval.
6. Define a function runProgram :: Program -> Int that takes a wellformed program comprising a list of function definitions and an expression
to be computed, and which uses eval to evaluate the expression. The
initial environment in the top-level call to eval should be []. To test
your runProgram function, execute the five programs provided in the
template. For example, when applied to eg2 it should return 120 (the
factorial of the maximum of 5 and 3, i.e. 5! = 5 4 3 2 1).
[2 marks]
7. Using the function arities (and/or arity) above, define a function isValid
:: Program -> Bool that checks whether all user-defined function applications have the correct number of arguments. The function should
return True if and only if (iff) the program is well-formed in this sense.
[5 marks]