1. Hello, World!
To get started, you must first install an Emerald compiler. You can follow the link given on the course web site, or, if you are familiar with Docker, you can also use the one-liner given here.
Once you are at a terminal with the commands ec
and emx
in your
PATH
, verify that you can compile a Hello, World!
program.
Copy the following code into a file called hello.m
:
const hello <- object hello
initially
stdout.putstring["Hello, World!\n"]
end initially
end hello
Much like the Java toolchain, the Emerald compiler generates machine
code for an Emerald virtual machine. Use ec
to compile an Emerald
program, and ex
to execute the resulting Emerald binary:
$ ec hello.m
$ emx hello.x
Hello, World!
2. Types
An Emerald type contains a collection of operation signatures. An
operation signature consists of an operation name, the names and types
of its parameters, and the return value name and type. Declare a
const
variable set
of type SimpleCollection
, and set it to an
object that conforms to SimpleCollection
:
const SimpleCollection <- typeobject SimpleCollection
operation add [ name : String ] -> [ res : Boolean ]
function contains [ name : String ] -> [ res : Boolean ]
operation remove [ name : String ] -> [ res : Boolean ]
end SimpleCollection
const set : SimpleCollection <- object set
% TODO
end set
Don't spend time actually implementing a data structure that
nominally makes sense. Merely declare a data structure that
conforms to the given typeobject
. Actually implementing the data
structure is subject matter for another exercise.
To test, you might end your Emerald source file with something like this:
const main <- object main
initially
stdout.putstring[set.contains["x"].asString || "\n"]
end initially
end main
This showcases the asString
method on booleans, and the
concatenation (||
) method on strings.
What happens when you add methods to the set
object? Are you allowed
to do that? Are you allowed to omit any of the methods declared in
SimpleCollection
?
3. Classes
Emerald has no notion of a “class”, but there is syntactic construct
called class
which can be used to achieve much of the same
behaviour.
For instance, here is a Person
, that has a name:
const Person <- class Person [ name : String ]
export function getname -> [ res : String ]
res <- name
end getname
end Person
The class
construct creates a method create
under the hood which
takes a String
as a parameter. This means that we can create
Person
objects as follows:
const main <- object main
initially
const oleks <- Person.create["Oleks"]
stdout.putstring[oleks.getname || "\n"]
const eric <- Person.create["Eric"]
stdout.putstring[eric.getname || "\n"]
end initially
end main
Change the definition of the Person
constant, so that it does not
use the keyword class
, but achieves the same behaviour as above
(i.e., you should not have to change the definition of the main
constant).
4. Inheritance
Emerald also supports inheritance.
For instance, here is how we would declare a Teacher
deriving from a
Person
:
const Person <- class Person [ name : String ]
export function getname -> [ res : String ]
res <- name
end getname
end Person
const Teacher <- class Teacher ( Person ) [ position: String ]
export function getposition -> [ res : String ]
res <- position
end getposition
end Teacher
const main <- object main
initially
const oleks <- Person.create["Oleks"]
stdout.putstring[oleks.getname || "\n"]
const eric <- Teacher.create["Eric", "Professor"]
stdout.putstring[eric.getname || "\n"]
stdout.putstring[eric.getposition || "\n"]
end initially
end main
Change the definition of the Person
and Teacher
constants, so that
they do not use the keyword class
, but achieve the same behaviour as
above (i.e., you should not have to change the definition of the
main
constant).
5. Covariance/Contravariance
Consider the following class hierarchy:
const Person <- class Person [ name : String ]
export function getname -> [ res : String ]
res <- name
end getname
end Person
const Student <- class Student ( Person )
export function ask [ a : String ] -> [ b : Any ]
b <- a % Nothing but an echo.
end ask
end Student
const Teacher <- class Teacher ( Person )
export function ask [ a : Any ] -> [ b : String ]
b <- "Always pass on what you have learned."
end ask
end Teacher
const main <- object main
initially
const oleks <- Student.create["Oleks"]
stdout.putstring[oleks.getname || "\n"]
const eric <- Teacher.create["Eric"]
stdout.putstring[eric.getname || "\n"]
end initially
end main
That is, both teachers and students are people, but when you ask a teacher anything, you get a concrete response, and when you ask a student something concrete, you can get anything in response.
- Does
Student
conform toTeacher
, or doesTeacher
conform toStudent
, or both? Showcase your response by modifying themain
constant above. - What does this tell you about the relationship between notions of conformance and subtyping?
- Draw a UML diagram over this class hierarchy, denoting also the
argument, and return types of the
ask
methods. Use additional inheritance arrows to show the direction of inheritance of the argument and return types of theask
method. How do the directions of the inheritance arrows relate to the terms covariant and contravariant?
6. Generating Primes
Implement a class PrimGen
that conforms to the following typeobject
:
const Gen <- typeobject Gen
operation next -> [ res : Integer ]
end Gen
PrimGen
should generate prime numbers in the range [2;100]
, in
order, and restart at 2
after having reached the last prime number
before 100
.
You can use the Sieve of Eratosthenes algorithm.
- What is the time and space complexity of your implementation?
- How many prime numbers are the in the range
[2;100]
?
7. Data Structures
Implement a proper data structure that not only conforms to
SimpleCollection
above, but also does what it nominally implies —
maintains a set of strings. You are free to choose the underlying
implementation. Create a handful test-cases to showcase that your
implementation works.
8. Keywords
After completing these exercises, you should be familiar with the following keywords. If not, read up on them in the language report.
- Object
- Operation
- Function
- Object Constructor
- Conformity
- Class
- Inheritance
9. Tips
You can use the $
as syntactic sugar for .get
.
For instance, you can write eric$name
in place of eric.getname
above.