INF5510 Exercise Set 1
v18.2 (2018-01-25)
Sølve Johnsen
Oleks Shturmov

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.

  1. Does Student conform to Teacher, or does Teacher conform to Student, or both? Showcase your response by modifying the main constant above.
  2. What does this tell you about the relationship between notions of conformance and subtyping?
  3. 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 the ask 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.

  1. What is the time and space complexity of your implementation?
  2. 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.