Martin Sulzmann
Translating methods, interfaces and structural subtyping in Go
Translating generics in Go
Arithmetic expressions in Rust - Evaluator
Arithmetic expressions in Rust - Type checker
OO in Java versus Go
Monads in the OO context
Dependent types
Recall the “run-time method look up” (RT) and “dictionary-translation” (DT) approach covered here
Compare the run-time performance of RT and DT (e.g. call “sumArea” in a loop and measure which version runs faster)
Apply the RT and DT approach to one further example of your own choice.
Extend RT and DT to deal with type assertions. See example below.
Extend RT and DT to deal with type bounds. See example below.
Summarize your findings in a short document (you could set up a github repo).
type square struct {
length int
}
func (s square) area() int {
return s.length * s.length
}
type shape interface {
area() int
}
func sumAreaVariant(x, y shape) int {
z := y.(square)
// Not every shape value can be turned into a square.
// Above might fail at run-time.
return x.area() + y.area() + z.length
}
func testSumAreaVariant() {
fmt.Printf("%d", sumAreaVariant(square{1}, square{2}))
// Type checks cause square <= shape
}We consider the translaton of programs with universal type parameters (aka “generics”). We assume that there are no type bounds and no uses of structural subtyping.
There are two established translation methods where source Go programs with generics are translated to a more simple target language.
Monomorphization where we generate type-specific instances
Generic translation where each type parameter is replaced by ‘interface{}’.
Apply both translation methods to the three examples below. The first example contains a sketch for each translation method.
Is it possible to apply both translation methods for each example? Why (not)?
What programming language features are required for target programs. Hint: The generic translation method might need to rely on structural subtyping and type assertions.
Summarize your findings in a short document (you could set up a github repo).
type node[T any] struct {
val T
next *node[T]
}
func mkNode[T any](v T) *node[T] {
return &node[T]{val: v, next: nil}
}
func insert[T any](n *node[T], v T) {
n.next = mkNode[T](v)
}
// n > 0
func mkList[T any](n int, v T) *node[T] {
head := mkNode(v)
current := head
for i := 0; i < n-1; i++ {
insert(current, v)
current = current.next
}
return head
}
func len[T any](n *node[T]) int {
i := 0
for n != nil {
i++
n = n.next
}
return i
}
func testNode() {
n1 := mkList[int](10, 1)
fmt.Printf("\n%d", len(n1))
n2 := mkList[bool](10, true)
fmt.Printf("\n%d", len(n2))
}
// Generic translation (sketch)
type node_G struct {
val interface{}
next *node_G
}
func mkNode_G(v interface{}) *node_G {
return &node_G{val: v, next: nil}
}
// Monomorphization (sketch)
type node_int struct {
val int
next *node_int
}
func mkNode_int(v int) *node_int {
return &node_int{val: v, next: nil}
}Consider Haskell to implement other languages
Represent the Haskell data type in Rust
Implement the evaluator in Rust including simplifications (e.g. “0 * x” shall immediately yield 0).
Draw a comparison between Haskell and Rust
Summarize your findings in a short document (you could set up a github repo).
Consider Haskell to implement other languages
Represent the Haskell data type in Rust
Implement the type checker in Rust
Draw a comparison between Haskell and Rust
Summarize your findings in a short document (you could set up a github repo).
We compare inheritance in Java (nominal subtyping, virtual methods) versus Go interfaces and structural subtyping
Consider several Java examples (of your own choice) that make use of nominal subtyping and virtual methods
Re-implement these examples in Go
Draw a comparison between Java and Go
Summarize your findings in a short document (you could set up a github repo).
Monads allow for the systematic control of side effects where
computations and their results are described via types, and
side-effecting computations are build via functional composition of primitive monad functions.
Thanks to functional composition and the ability to overload monadic
operators such as >>= and return,
monadic programs “almost” look like imperative programs.
Your task is to collect examples of monads in the OO context (Java, C++, …)
To get started you find a below a description of the “Maybe” monad in Haskell as well as an implementation in C++.
Summarize your findings in a short document (you could set up a github repo).
Monads allow for the systematic control of side effects
Computations and their results are described via types, and
side-effecting computations are build via functional composition of primitive monad functions.
-- (x/y)/z
example x y z = case (divSafe x y) of
Just res -> case (divSafe res z) of
Just res2 -> "Just " ++ show res2
Nothing -> "Nothing"This looks ugly!
Checking the result “manually” is rather clumsy!
exampleM x y z = do res <- divSafe x y
res2 <- divSafe res z
return res2
-- "do" Syntax desugared in a sequence of "bind" (>>=) operations
-- In our case,
-- (>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
exampleM2 x y z = (divSafe x y)
>>=
(\res -> divSafe res z
>>=
(\res -> return res))import Control.Applicative
import Control.Monad
-- Function with side-effect.
-- We raise an exception if we attempt to divide by zero.
divInt :: Int -> Int -> Int
divInt _ 0 = error "can't divide by zero"
divInt n m = n `div` m
divSafe :: Int -> Int -> Maybe Int
divSafe _ 0 = Nothing
divSafe n m = Just $ n `div` m
-- Maybe is a monad, can use fail/return instead of Nothing/Just.
divSafe2 :: Int -> Int -> Maybe Int
divSafe2 _ 0 = fail ""
divSafe2 n m = return $ n `div` m
example x y z = case (divSafe x y) of
Just res -> case (divSafe res z) of
Just res2 -> "Just " ++ show res2
Nothing -> "Nothing"
exampleM x y z = do res <- divSafe x y
res2 <- divSafe res z
return res2
-- "do" Syntax desugared in a sequence of "bind" (>>=) operations
-- In our case,
-- (>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
exampleM2 x y z = (divSafe x y)
>>=
(\res -> divSafe res z
>>=
(\res -> return res))
runMaybe comp = case comp of
Just x -> "The result is: " ++ show x
Nothing -> "Failure"
-- Defining our own simple 'maybe' monad
data Res a = Something a | Fail String deriving Show
instance Monad Res where
return x = Something x
p >>= f = case p of
(Fail e) -> Fail e
(Something x) -> f x
instance Applicative Res where
pure = return
x <*> y = do f <- x
a <- y
return (f a)
instance Functor Res where
fmap f x = do a <- x
return (f a)template<typename T>
class Optional {
bool b;
T val;
public:
Optional() : b(false) {}
Optional(T v) : val(v), b(true) {}
bool isJust() { return b; }
bool isNothing() { return !b; }
T fromJust() { return val; }
template<typename S>
Optional<S> bind(function<Optional<S>(T)> f) {
if (this->isNothing()) {
return *this;
} else {
auto v = this->fromJust();
return f(v);
}
}
void print() {
if (this->isNothing()) {
cout << "\n Invalid result";
} else {
cout << "\n" << this->fromJust();
}
}
}; // Optional class
template<typename T>
Optional<T> ret(T x) {
return Optional<T>(x);
}
template<typename T>
Optional<T> fail() {
return Optional<T>();
}“bind” is a method
“return/fail” are separate functions
Our running example “(x/y)/z” rephrased in terms of C++.
Optional<int> exampleM(int x, int y, int z) {
return divSafe(x,y).bind<int>([z](int res) -> Optional<int> {
return divSafe(res,z).bind<int>([](int res2) -> Optional<int> { return ret(res2);});});
}Looks rather ugly (there’s no “do” notation)
Also rather clumsy as we don’t have Haskell’s powerful type inference mechanism.
template<typename T>
class Optional {
Optional<T> div(T x) {
if (this->isNothing() || x == 0) {
return *this;
} else {
auto v = this->fromJust();
return Optional(v/x);
}
}
};/*
Monads. A concept none in functional languages such as Haskell.
Turns out, monads are also useful in imperative languages.
- Systematic control of side effects.
- We use types to describe effects.
- Effectful computations are built via function composition.
Benefits.
Systematic control of side effects.
What and where side effects take place can be easily read out of the program text.
User needs to follow the "monadic" design pattern.
Example: The Optional (aka Maybe) monad.
Side effect we wish to control:
Result is successful ("just some value") or fails ("nothing").
*/
#include <functional>
#include <stdio.h>
#include <iostream>
using namespace std;
// Haskell's Maybe rephrased in C++.
template<typename T>
class Optional {
bool b;
T val;
public:
Optional() : b(false) {}
Optional(T v) : val(v), b(true) {}
bool isJust() { return b; }
bool isNothing() { return !b; }
T fromJust() { return val; }
template<typename S>
Optional<S> bind(function<Optional<S>(T)> f) {
if (this->isNothing()) {
return *this;
} else {
auto v = this->fromJust();
return f(v);
}
}
void print() {
if (this->isNothing()) {
cout << "\n Invalid result";
} else {
cout << "\n" << this->fromJust();
}
}
// Assumes type T supports division.
Optional<T> div(T x) {
if (this->isNothing() || x == 0) {
return *this;
} else {
auto v = this->fromJust();
return Optional(v/x);
}
}
}; // Optional class
template<typename T>
Optional<T> ret(T x) {
return Optional<T>(x);
}
template<typename T>
Optional<T> fail() {
return Optional<T>();
}
// Fail safe integer division
Optional<int> divSafe(int x, int y) {
if (y == 0) {
return Optional<int>();
} else {
return Optional<int>(x / y);
}
}
// (x/y)/z
Optional<int> example(int x, int y, int z) {
auto res = divSafe(x,y);
if (res.isNothing()) {
cout << "\n Invalid result";
}
auto res2 = divSafe(res.fromJust(),z);
return res2;
}
// Optional is a monad.
// Use functional composition.
Optional<int> exampleM(int x, int y, int z) {
return divSafe(x,y).bind<int>([z](int res) -> Optional<int> {
return divSafe(res,z).bind<int>([](int res2) -> Optional<int> { return ret(res2);});});
}
// Method chaining instead of functional composition.
Optional<int> exampleM2(int x, int y, int z) {
return Optional<int>(x).div(y).div(z);
}
int main() {
int x,y,z;
x = 12; y = 2; z = 3;
example(x,y,z).print();
exampleM(x,y,z).print();
exampleM2(x,y,z).print();
}Look for examples of “dependent types” in Rust.
Rephrase the Dependent types in Haskell in Rust (as many Haskell code parts as possible).
Summarize your findings in a short document.