How to type a compiler? - functional-programming

Take two abstract machines and two instructions the semantics of which is encoded in the following fictional (functional) language:
class incr;
// preserves #code, increments #pc, increments #x
fn execA(state: {pc: i32, code: {(pc): incr}, x: i32}): {code: `state.code`, pc: `state.pc+1`, x: `state.x+1`}
class incrBy2;
// preserves #code, increments #pc, adds 2 to #x
fn execB(state: {pc: i32, code: {(pc): incrBy2}, x: i32}): {code: `state.code`, pc: `state.pc+1`, x: `state.x+2`}
Where {(i): i32} is a subtype of [string, i32, f64] iff i == 1, so the state.code is a tuple of instructions. Moreover we have expression types which are singleton types inhabited only by the given expression (think of path dependent types in scala).
Statement: given the code [incrBy2] it can be rewritten to [incr, incr]
Question: How would you formalize (in our fictional language) the above statement?

Related

How can I convert a Vec<String> to a &[&gtk::ToValue]? [duplicate]

This question already has answers here:
Is there any way to return a reference to a variable created in a function?
(5 answers)
Cannot call rusqlite's query because it expects the type &[&rusqlite::types::ToSql]
(1 answer)
Closed 5 years ago.
I've been trying many things from nested loops and sequential duplicated nested loops to this:
pub fn values_to_references<'a>(values_vector: Vec<String>) -> Vec<&'a gtk::ToValue> {
values_vector
.into_iter()
.map(| item: String | &item.to_value() as &ToValue)
.collect()
}
But whatever I try, the compiler always tells me that the lifetime of item is too short:
borrowed value must be valid for the lifetime 'a as defined on the body at 87:89...
borrowed value does not live long enough (does not live long enough)
I need to convert to those references in order to add Strings to a gtk::TreeModel (gtk::TreeStore), which will balk at anything other than that it seems. All examples I could find use references to static strings, which are known at compile time. That's not very useful. My strings come from a JSON file and cannot be known at compile time.
I want to be able to do the following with the gtk::TreeModel:
model.insert_with_values(
None,
None,
&[0, 1, 2, 3],
to_value_items.as_slice());
How can I achieve this?
Example Code
More code from my attempts to add stuff to a gtk::TreeStore or gtk::TreeModel:
pub fn add_to_tree_store(tree_store: &TreeStore, row: Vec<String>) {
tree_store.insert_with_values(
None,
None,
&[0, 1, 2, 3],
VocTreeView::strings_to_ampersand_str(row)
.iter()
.map(|x| x as &ToValue)
.collect());
}
pub fn strings_to_ampersand_str<'res_ref>(values_vector: Vec<String>) -> Vec<&'res_ref str> {
let append_values: Vec<_> = values_vector.iter().map(|x| &x[..]).collect();
append_values
}
This fails at collect():
a collection of type `&[&gtk::ToValue]` cannot be built from an iterator over elements of type `&gtk::ToValue` [E0277]
the trait bound `&[&gtk::ToValue]: std::iter::FromIterator<&gtk::ToValue>` is not satisfied (the trait `std::iter::FromIterator<&gtk::ToValue>` is not implemented for `&[&gtk::ToValue]`) [E0277]
Seems the same problem is again biting me.
Example Code 2
pub fn add_to_tree_store(tree_store: &TreeStore, row: Vec<String>) {
tree_store.insert_with_values(
None,
None,
&[0, 1, 2, 3],
VocTreeView::values_to_references(&row)
.iter()
.map(|x| x as &gtk::ToValue)
.collect());
}
pub fn values_to_references(values_vector: &[String]) -> Vec<&gtk::ToValue> {
values_vector
.into_iter()
.map(|item| item as &gtk::ToValue)
.collect()
}
This runs into an error at x inside map and collect as well:
At x:
required for the cast to the object type `gtk::ToValue` [E0277]
required because of the requirements on the impl of `gtk::ToValue` for `&gtk::ToValue` [E0277]
required because of the requirements on the impl of `glib::value::SetValue` for `&gtk::ToValue` [E0277]
the trait bound `gtk::ToValue: glib::value::SetValue` is not satisfied (the trait `glib::value::SetValue` is not implemented for `gtk::ToValue`) [E0277]
At collect:
a collection of type `&[&gtk::ToValue]` cannot be built from an iterator over elements of type `&gtk::ToValue` [E0277]
the trait bound `&[&gtk::ToValue]: std::iter::FromIterator<&gtk::ToValue>` is not satisfied (the trait `std::iter::FromIterator<&gtk::ToValue>` is not implemented for `&[&gtk::ToValue]`) [E0277]

How do I write a function that adds an element to a vector, allowing the element to be changed before insertion?

I'm trying to make a simple example involving a vector of structs to learn Rust. All examples of vectors in the Rust literature I've found only use vectors of integers.
I want to write a function aimed at filling a vector, allowing the possibility of the element to be inserted to be changed, I can't figure out what to do. I always got a compiler error[E0308]: mismatched types on
the push method, because elem is a reference to a Point. So
push() needs a Point structure because v is a vector of Point
but if I want to modify elem, I need to pass a (mutable?) reference
What is the right thing to do?
// structure used everywhere in Rust examples
#[derive(Debug)]
struct Point {
x: i16,
y: i16
}
fn add_element(v: &mut Vec<Point>, elem: &Point) {
// modify element
elem.x = 0;
// add element
v.push(elem);
}
// this example is meant to study a vector of structs
fn main() {
// declare 2 points. By default, live on the stack
let origin = Point {x:0, y:0};
println!("origin address\t: {:p}", &origin);
let mut p1 = Point {x:1, y:1};
println!("p1 address\t: {:p}", &p1);
// declare a new vector of structs. Allocation is made in the heap
// declare mutable because we'll add elements to vector
let mut v: Vec<Point> = Vec::new();
// add points
add_element(&mut v, &origin);
add_element(&mut v, &p1);
// change p1
p1.x = 2;
p1.y = 2;
}
Let's read the error messages together:
error[E0308]: mismatched types
--> src/main.rs:10:12
|
10 | v.push(elem);
| ^^^^ expected struct `Point`, found &Point
|
= note: expected type `Point`
= note: found type `&Point`
The code is attempting to store a reference to a Point in a Vec that is declared to hold entire Points. Since Rust is a statically- and strongly- typed language, the compiler tells you that you cannot do that. The fix is to accept a Point by value:
fn add_element(v: &mut Vec<Point>, elem: Point)
This leads to the next error:
error: cannot assign to immutable field `elem.x`
--> src/main.rs:9:5
|
9 | elem.x = 0;
| ^^^^^^^^^^
You cannot change members of elem because it is not marked as mutable. Mutability of a value is a property of the binding, so let's do that:
fn add_element(v: &mut Vec<Point>, mut elem: Point)
Then change the calling of that function to adapt:
fn main() {
let origin = Point { x: 0, y: 0 };
let p1 = Point { x: 1, y: 1 };
let mut v = Vec::new();
add_element(&mut v, origin);
add_element(&mut v, p1);
}
Note that neither origin nor p1 need to be mutable because this function doesn't modify either while it owns it. It transfers ownership to add_element, which chooses to make it mutable.
but if I want to modify elem, I need to pass a (mutable?) reference
As you can see, you can simply make the elem parameter mutable when transferring the entire value to the function. Since the function owns that value, it has full control over it, including choosing to make it mutable.

Rust Vector of Traits: cast each trait

I have a problem casting a vector of traits into a vector of different traits.
Using the approach of Type-casting arrays/vectors in Rust , I basically tried the following:
trait ParentTrait {}
trait ChildTrait: ParentTrait {}
fn main() {
let mut children: Vec<Box<ChildTrait>> = vec![];
let parents = children.iter().map(|&e| e as Box<ParentTrait>);
}
Now this does not compile, it results in
error: the trait `core::kinds::Sized` is not implemented for the type `ChildTrait`
[...]
error: the trait `ParentTrait` is not implemented for the type `ChildTrait`
[...]
(The second errorline is buggy behaviour of the compiler, I guess?)
I tried various other flavors of References / Boxes and could not get it to work.
What am I doing wrong here,
is this even the correct approach with newer versions of rust (0.13)?
Trait objects are very strange beasts.
What is a Box<ChildTrait>? Box<T> is literally a wrapper for a *mut T. Therefore, a Box<ChildTrait> wraps a *mut ChildTrait. Because ChildTrait names a trait, ChildTrait is an object type. A pointer to an object type is represented by a pair of pointers: a pointer to the vtable for that trait and only that trait, and a pointer to the actual value.
When we inherit a trait from another trait, that doesn't mean we can obtain a pointer to the vtable for the first trait from a pointer to the vtable for the second trait. This is why the compiler complains that
the trait `ParentTrait` is not implemented for the type `ChildTrait`
We can, however, manually implement a trait for an object type. Because object types are unsized, we must first allow ParentTrait to be implemented for unsized types:
trait ParentTrait for Sized? {}
Then we can provide an impl of ParentTrait for the ChildTrait object type:
impl<'a> ParentTrait for ChildTrait+'a {}
If we try to compile now, we get different errors:
<anon>:9:40: 9:42 error: cannot move out of dereference of `&`-pointer
<anon>:9 let parents = children.iter().map(|&e| e as Box<ParentTrait>);
^~
<anon>:9:41: 9:42 note: attempting to move value to here
<anon>:9 let parents = children.iter().map(|&e| e as Box<ParentTrait>);
^
<anon>:9:41: 9:42 help: to prevent the move, use `ref e` or `ref mut e` to capture value by reference
<anon>:9 let parents = children.iter().map(|&e| e as Box<ParentTrait>);
We can use into_iter instead of iter to consume the initial Vec:
fn main() {
let mut children: Vec<Box<ChildTrait>> = vec![];
let parents = children.into_iter().map(|e| e as Box<ParentTrait>);
}
But then we get an internal compiler error:
error: internal compiler error: trying to take the sizing type of ChildTrait, an unsized type
note: the compiler unexpectedly panicked. this is a bug.
note: we would appreciate a bug report: http://doc.rust-lang.org/complement-bugreport.html
note: run with `RUST_BACKTRACE=1` for a backtrace
task 'rustc' panicked at 'Box<Any>', /build/rust-git/src/rust/src/libsyntax/diagnostic.rs:175
The same error also occurs with this code:
fn main() {
let mut children: Vec<Box<ChildTrait>> = vec![];
let parents = children.iter().map(|e| &**e as &ParentTrait);
}
At this point, I don't know if, after fixing the ICE, this would compile successfully or not.

Mutable vectors in struct

I'm trying to get a graph clustering algorithm to work in Rust. Part of the code is a WeightedGraph data structure with an adjacency list representation. The core would be represented like this (shown in Python to make it clear what I'm trying to do):
class Edge(object):
def __init__(self, target, weight):
self.target = target
self.weight = weight
class WeightedGraph(object):
def __init__(self, initial_size):
self.adjacency_list = [[] for i in range(initial_size)]
self.size = initial_size
self.edge_count = 0
def add_edge(self, source, target, weight):
self.adjacency_list[source].append(Edge(target, weight))
self.edge_count += 1
So, the adjacency list holds an array of n arrays: one array for each node in the graph. The inner array holds the neighbors of that node, represented as Edge (the target node number and the double weight).
My attempt to translate the whole thing to Rust looks like this:
struct Edge {
target: uint,
weight: f64
}
struct WeightedGraph {
adjacency_list: ~Vec<~Vec<Edge>>,
size: uint,
edge_count: int
}
impl WeightedGraph {
fn new(num_nodes: uint) -> WeightedGraph {
let mut adjacency_list: ~Vec<~Vec<Edge>> = box Vec::from_fn(num_nodes, |idx| box Vec::new());
WeightedGraph {
adjacency_list: adjacency_list,
size: num_nodes,
edge_count: 0
}
}
fn add_edge(mut self, source: uint, target: uint, weight: f64) {
self.adjacency_list.get(source).push(Edge { target: target, weight: weight });
self.edge_count += 1;
}
}
But rustc gives me this error:
weightedgraph.rs:24:9: 24:40 error: cannot borrow immutable dereference of `~`-pointer as mutable
weightedgraph.rs:24 self.adjacency_list.get(source).push(Edge { target: target, weight: weight });
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
So, 2 main questions:
1. How can I get the add_edge method to work?
I'm thinking that WeightedGraph is supposed to own all its inner data (please correct me if I'm wrong). But why can add_edge not modify the graph's own data?
2. Is ~Vec<~Vec<Edge>> the correct way to represent a variable-sized array/list that holds a dynamic list in each element?
The tutorial also mentions ~[int] as vector syntax, so should it be: ~[~[Edge]] instead? Or what is the difference between Vec<Edge> and ~[Edge]? And if I'm supposed to use ~[~[Edge]], how would I construct/initialize the inner lists then? (currently, I tried to use Vec::from_fn)
The WeightedGraph does own all its inner data, but even if you own something you have to opt into mutating it. get gives you a & pointer, to mutate you need a &mut pointer. Vec::get_mut will give you that: self.adjacency_list.get_mut(source).push(...).
Regarding ~Vec<Edge> and ~[Edge]: It used to be (until very recently) that ~[T] denoted a growable vector of T, unlike every other type that's written ~... This special case was removed and ~[T] is now just a unique pointer to a T-slice, i.e. an owning pointer to a bunch of Ts in memory without any growth capability. Vec<T> is now the growable vector type.
Note that it's Vec<T>, not ~Vec<T>; the ~ used to be part of the vector syntax but here it's just an ordinary unique pointer and represents completely unnecessary indirection and allocation. You want adjacency_list: Vec<Vec<Edge>>. A Vec<T> is a fully fledged concrete type (a triple data, length, capacity if that means anything to you), it encapsulates the memory allocation and indirection and you can use it as a value. You gain nothing by boxing it, and lose clarity as well as performance.
You have another (minor) issue: fn add_edge(mut self, ...), like fn add_edge(self, ...), means "take self by value". Since the adjacency_list member is a linear type (it can be dropped, it is moved instead of copied implicitly), your WeightedGraph is also a linear type. The following code will fail because the first add_edge call consumed the graph.
let g = WeightedGraph::new(2);
g.add_edge(1, 0, 2); // moving out of g
g.add_edge(0, 1, 3); // error: use of g after move
You want &mut self: Allow mutation of self but don't take ownership of it/don't move it.
get only returns immutable references, you have to use get_mut if you want to modify the data
You only need Vec<Vec<Edge>>, Vec is the right thing to use, ~[] was for that purpose in the past but now means something else (or will, not sure if that is changed already)
You also have to change the signature of add_edge to take &mut self because now you are moving the ownership of self to add_edge and that is not what you want

Function with different argument types

I read about polymorphism in function and saw this example
fun len nil = 0
| len rest = 1 + len (tl rest)
All the other examples dealt with nil arg too.
I wanted to check the polymorphism concept on other types, like
fun func (a : int) : int = 1
| func (b : string) : int = 2 ;
and got the follow error
stdIn:1.6-2.33 Error: parameter or result constraints of clauses don't agree
[tycon mismatch]
this clause: string -> int
previous clauses: int -> int
in declaration:
func = (fn a : int => 1: int
| b : string => 2: int)
What is the mistake in the above function? Is it legal at all?
Subtype Polymorphism:
In a programming languages like Java, C# o C++ you have a set of subtyping rules that govern polymorphism. For instance, in object-oriented programming languages if you have a type A that is a supertype of a type B; then wherever A appears you can pass a B, right?
For instance, if you have a type Mammal, and Dog and Cat were subtypes of Mammal, then wherever Mammal appears you could pass a Dog or a Cat.
You can achive the same concept in SML using datatypes and constructors. For instance:
datatype mammal = Dog of String | Cat of String
Then if you have a function that receives a mammal, like:
fun walk(m: mammal) = ...
Then you could pass a Dog or a Cat, because they are constructors for mammals. For instance:
walk(Dog("Fido"));
walk(Cat("Zoe"));
So this is the way SML achieves something similar to what we know as subtype polymorphism in object-oriented languajes.
Ad-hoc Polymorphysm:
Coercions
The actual point of confusion could be the fact that languages like Java, C# and C++ typically have automatic coercions of types. For instance, in Java an int can be automatically coerced to a long, and a float to a double. As such, I could have a function that accepts doubles and I could pass integers. Some call these automatic coercions ad-hoc polymorphism.
Such form of polymorphism does not exist in SML. In those cases you are forced to manually coerced or convert one type to another.
fun calc(r: real) = r
You cannot call it with an integer, to do so you must convert it first:
calc(Real.fromInt(10));
So, as you can see, there is no ad-hoc polymorphism of this kind in SML. You must do castings/conversions/coercions manually.
Function Overloading
Another form of ad-hoc polymorphism is what we call method overloading in languages like Java, C# and C++. Again, there is no such thing in SML. You may define two different functions with different names, but no the same function (same name) receiving different parameters or parameter types.
This concept of function or method overloading must not be confused with what you use in your examples, which is simply pattern matching for functions. That is syntantic sugar for something like this:
fun len xs =
if null xs then 0
else 1 + len(tl xs)
Parametric Polymorphism:
Finally, SML offers parametric polymorphism, very similar to what generics do in Java and C# and I understand that somewhat similar to templates in C++.
So, for instance, you could have a type like
datatype 'a list = Empty | Cons of 'a * 'a list
In a type like this 'a represents any type. Therefore this is a polymorphic type. As such, I could use the same type to define a list of integers, or a list of strings:
val listOfString = Cons("Obi-wan", Empty);
Or a list of integers
val numbers = Cons(1, Empty);
Or a list of mammals:
val pets = Cons(Cat("Milo", Cons(Dog("Bentley"), Empty)));
This is the same thing you could do with SML lists, which also have parametric polymorphism:
You could define lists of many "different types":
val listOfString = "Yoda"::"Anakin"::"Luke"::[]
val listOfIntegers 1::2::3::4::[]
val listOfMammals = Cat("Misingo")::Dog("Fido")::Cat("Dexter")::Dog("Tank")::[]
In the same sense, we could have parametric polymorphism in functions, like in the following example where we have an identity function:
fun id x = x
The type of x is 'a, which basically means you can substitute it for any type you want, like
id("hello");
id(35);
id(Dog("Diesel"));
id(Cat("Milo"));
So, as you can see, combining all these different forms of polymorphism you should be able to achieve the same things you do in other statically typed languages.
No, it's not legal. In SML, every function has a type. The type of the len function you gave as an example is
fn : 'a list -> int
That is, it takes a list of any type and returns an integer. The function you're trying to make takes and integer or a string, and returns an integer, and that's not legal in the SML type system. The usual workaround is to make a wrapper type:
datatype wrapper = I of int | S of string
fun func (I a) = 1
| func (S a) = 2
That function has type
fn : wrapper -> int
Where wrapper can contain either an integer or a string.

Resources