CS558 Homework #8
Due 4:00 PM, Tuesday, March 11, 2014
Homework must be submitted via D2L. All submitted files (2 for this assignment sol8A.e8
and sol8B.e8) must be submitted in the
appropriate D2L directory in the drop box HW8. It is your responsibility to submit
the homework in the proper format with the proper names.
All programs mentioned can be downloaded from the this document
- Hw8.hs. A definitional interpreter (in Haskell) for language E8.
- test1.e8. A file with E8 programs illustrating the examples in this homework description.
In this homework we consider a variant of simple un typed object oriented language we call E8.
The language, E8, has the following features:
- First class objects which implement encapsulation. Every object
is solely characterized by the requests it can respond to.
- Inheritance. Objects can inherit behavior from other objects.
- Dynamic binding. The binding of behaviors to an object is done dynamically at run time.
- Naming. The language separates the naming of things from the encapsulation of
things in objects.
- A simple exception system, along the lines of Homework4
- he use of dynamic typing. Typing issues are checked at run time
E8's concretized
abstract syntax is given by the following grammar:
prog := { decl } 'init' exp
decl := '(' 'val' exp ')' -- a variable
| '(' 'ref' [ exp ] ')' -- mutable variable
| '(' 'fun' '(' {var } ')' exp ')' -- function
| '(' { 'left' | 'right' | 'non' } operator ')' -- associativity of operator
field := '(' 'def' exp ')' -- a constant field
| '(' 'var' [ exp ] ')' -- a mutable field
| '(' 'method' '(' {var } ')' exp ')' -- a method
exp :=
var
| int
| bool
| string
|
| '(' 'local' { decl } exp ')' -- Binding or naming mechanism
| '(' 'write' string exp ')' -- output
| '(' 'block' { exp } ')' -- sequencing
| '(' ':=' var exp ')' -- assigment to mutable variables
| '(' object [ 'inherits' exp ] { field } [ 'init' exp ] '}' -- object construtction
| '(' exp '.' fieldselection { exp } ')' -- message request
| 'self' -- the current ject
| 'done' -- the unit object
| 'super' -- the super class of the current object
| '(' 'return' var exp ')' -- An exception, non-standard return from a method.
| '(' 'if' exp exp exp ')'
| '(' 'while' exp exp ')'
| '(' '@' exp { exp } ')' -- function application
| '(' '\' '(' { var } ')' exp ')' -- lambda abstraction
| '(' operator { exp } ')' -- associative "infix" operators application
| '[]' -- the object with no behaviors
fieldselection := var | '(' var { exp } ')'
var := letter { letter | digit }
operator = ["+*-/!@#$%^&~=?<>:]+ -- a sequence of these operator characters
As before, comments may be included by enclosing them between '{' and
'}' characters, and they may be nested.
Note that the syntax for expressions has been divided into groups, where
each group supports one aspect of the language.
- Constants and variables.
- Imperative control structures.
- The core object calculus, for creating and sending objects messages.
- The derived forms. These language features can all be implemented
in terms of the core language. They are included because they make programming easier, but
they add no expressive power.
A sample file, which illustrates many of E8's features can be found here
test1.e8. This file illustrates several key points of the language
- Program structure.
A program consists of a sequence of declarations, and the main body, after
the keyword 'init'.
- Scope Rules
Note that every declaration is in scope in every other declaration.
This allows mutually recursive functions. The order of declarations in the file
does not matter.
- Key Features.
There are two main features. These features are meant to separate name binding from field declaration.
- Declarations appear in two places. A program is a sequence of declarations, and a 'local'
expression is a sequence of declarations. Declaration are used to name objects, and to bring them into
scope. As discussed above, all declarations in a single (program) or local
can be mutually recursive. Declarations perform name binding.
- Objects are composed of fields, a field describes the data stored in the object. The name of
a field is not brought into scope. But a field name can be used to select the value of one of
an objects fields. For example ((+ 3 4).asString) selects the "asString" field from the object 7.
Objects perform encapsulation.
- Note that field names and names bound by declarations are two different things.
- Object Evaluation.
Every program evaluates to an object. An object consists of a small amount of state
(for primitive types only) and a set of methods (or behaviors) that it will respond to.
For example observe the following E8 language interaction
enter Exp>
4
4 [+,-,*,/,asString,=,<=]
The valuation of the expression '3' evaluates to the object that responds to requests
for '+', '-', '*', '/', 'asString', '=' and '<=' .
- Language E8 by Example
Most important parts of the langauge have to do with object creation and method request.
- Object Creation.
Objects are created with the literal object expression. For example.
enter Exp>
(object (def x 5) (def y 2) (method add () (+ (self.x) (self.y))))
[x,y,add]
with three field, two constants and one method. Note because field definition does not
bring any names into scope. The method 'add' must use '(self.x)' to access that
objects 'x' field.
- Method requests.
Objects respond to messages. To program this we use a request expression.
(' exp '.' fieldselection { exp } ')' . Examples include
'(x.+ 4)' and '(x.(<= 5).and y)' . Here are a few sample interactions.
enter Exp>
((object (def x 5) (def y 2) (method add () (+ (self.x) (self.y)))) . add )
7 [+,-,*,/,asString,=,<=]
enter Exp>
((object (def x 5) (def y 2) (method add () (+ (self.x) (self.y)))) . bad )
Message not understood: bad
[x,y,add]
enter Exp>
("abc".(++ "123").len)
6 [+,-,*,/,asString,=,<=]
enter Exp>
("abc".(++ "123").len.(+ 1))
7 [+,-,*,/,asString,=,<=]
enter Exp>
Note that if an object does not respond to a behavior a runtime error is raised.
- Object Inheritance.
When an object is created it can inherit behavior from another object.
enter Exp>
done
done [asString]
enter Exp>
(object inherits done (def x 5) (def y 3))
done [x,y,asString]
enter Exp>
((object inherits done (def x 5) (def y 3)).asString)
"done" [sub,at,++,len,=,asString,<=]
Note that the simple object 'done' has only one behavior 'asString'. The anonymous object
inherits that behavior. And if it is called on the new object it behaves exactly as its
parent object.
- Method overriding.
One can override the behavior be adding a method
with the same name as one of the inherited methods, and one can access the old
methods by the use of 'super'. Consider the declaration of 'i3', and the interaction
in the scope of that declaration.
(val i3
(object inherits done
(def x 5)
(def y 3)
(method asString ()
(++ (super.asString)
" + "
(self.x.asString)
" + "
(self.y.asString)))))
enter Exp>
i3
done[x 5, y 3]
enter Exp>
(i3.asString)
"done + 5 + 3"
enter Exp>
- Object initialization.
Any object can have a optional initialization expression, that is executed after the object
is created. Often the initialization us used
to fill in the mutable variables of the object, but it can be used for
any side effect. For example below, we define an object 'cell' with a method 'new', which returns
a new object when called. This new object, has an initialization that increments the
'count' field of the enclosing cell object.
(val cell
(object (var count 0)
(method new (x)
(object
(def head x)
(def number (cell.count))
(var tail)
init (:= (cell.count) (+ 1 (cell.count)))))
))
enter Exp>
(cell.new 4)
[head 4, number 0, tail@6]
enter Exp>
(cell.new 5)
[head 5, number 1, tail@7]
Note that every new cell has a new (and different) number.
- Derived Forms.
The object core of language E8 is very expressive. In fact many of the features of
our earlier languages (E1, E2, etc.) can be directly expressed within the core calculus.
In fact all the features below are implemented by macro-expansion into core
language features.
| '(' 'if' exp exp exp ')'
| '(' 'while' exp exp ')'
| '(' '@' exp { exp } ')' -- function application
| '(' '\' '(' { var } ')' exp ')' -- lambda abstraction
| '(' operator { exp } ')' -- associative "infix" operators application
| '[]' -- the object with no behaviors
One can observe this using the :m expression in the interactive loop.
enter Exp>
:m (if tst thencl elsecl)
(if tst thencl elsecl) -- if then else
-->
(tst.ifTrueFalse
(\ () thencl)
(\ () elsecl))
enter Exp>
:m (\ (x y) (+ x y)) -- lambda abstarction
(\ (x y) (+ x y))
-->
(object (method apply (x y) (+ x y)))
enter Exp>
:m (@ f 4 5 6) -- function application
(@ f 4 5 6)
-->
(f.apply 4 5 6)
enter Exp>
:m (while tst body) -- while loops
(while tst body)
-->
(local
(fun loop ()
(tst.ifTrueFalse (\ () (block body (loop.apply) )) (\ () done)))
(loop.apply))
enter Exp>
:m (+ 2 3 4 5) -- left associative 'infix' operator
(+ 2 3 4 5)
-->
(((2.+ 3).+ 4).+ 5)
enter Exp>
:m (++ "a" "b" "c" "d") -- right associative 'infix' operator
(++ "a" "b" "c" "d")
-->
((("d".++ "c").++ "b").++ "a")
enter Exp>
:m [] -- the empty object with no behavior
[]
-->
(object)
What to Do
Your job in this homework is to write 2 small object oriented programs
- Define a tree data structure, and implement binary search.
- Implement binary search trees. Your tree should be similar to the Tree
type from question 2 of homework7.html.
(data (Tree a)
(#leaf)
(#Tip Int a)
(#node Int a (Tree a) (Tree a)))
Note the extra tip node. Your tree should be implemented using objects since their
are no data declarations in E8. Define a named class, tree, along the lines of the list class in
test1.e8. The tree class should have exactly 3 fields.
- A (def leaf ...)
- A (method tip (key value) ...)
- A (method node (key value left right) ...)
Each field, should in term, return another object. Just as nil and cons do in the list class.
- Don't forget you'll need one or more fields to test if a particular node is a leaf or a tip or a node.
- To write binary search you will also need to have search methods in all the constructors
of tree.
- You should also implement an asString methods for each of the constructors of trees.
- You must provide a tree (as a val declarations called testtree) with at
least 6 values.
- Thoroughly test both search and asString.
- Put all this in a file called sol8A.e8, and submit it to D2L.
- Define a mutable list structure, and use inheritance.
- This
will be similar to regular lists, except the tail must be a mutable variable.
- Write an asString method that print lists like [1,2,23,6] (hint
you might want to write more than one method to do this to ass the surrounding brackets).
- Write a method that reverses the list inplace by reversing the pointers.
This will need to use assignment to the tail of lists (:= (t.tail) y).
- You must provide a list (as a val declaration called testlist) with at
least 6 values.
- Now make a new type colorList, where each element is paired with a string telling its color.
Use inheritance. Your reversal methods should still work.
- Test all your code thoroughly.
- Put all this in a file called sol8B.e8, and submit it to D2L.