Skip to content

Commit ddfe370

Browse files
chore: initial commit
0 parents  commit ddfe370

File tree

5 files changed

+182
-0
lines changed

5 files changed

+182
-0
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/target

Cargo.lock

+16
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[package]
2+
name = "algo-j"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7+
8+
[dependencies]
9+
anyhow = { version = "*" }

src/lib.rs

+153
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
#![feature(box_patterns)]
2+
3+
/// Implementation of the Hindley-Milner type system
4+
/// https://en.wikipedia.org/wiki/Hindley%E2%80%93Milner_type_system
5+
/// using the Algorithm J
6+
use anyhow::Result;
7+
use std::{
8+
collections::HashMap,
9+
sync::atomic::{AtomicUsize, Ordering},
10+
};
11+
12+
#[derive(PartialEq, Hash, Eq)]
13+
enum Type {
14+
/// _|_
15+
Unit,
16+
/// a
17+
Var(Scope),
18+
/// a -> b
19+
Arrow(Box<Type>, Box<Type>),
20+
}
21+
22+
/// Efficient generalization with levels
23+
/// https://okmij.org/ftp/ML/generalization.html#levels
24+
static LEVEL: AtomicUsize = AtomicUsize::new(0);
25+
static TYPEVAR_LEVEL: AtomicUsize = AtomicUsize::new(0);
26+
27+
#[derive(PartialEq, Hash, Eq)]
28+
struct BoundVar {
29+
bounded: Type,
30+
level: i32,
31+
}
32+
33+
impl BoundVar {
34+
pub fn enter_level() {
35+
LEVEL.fetch_add(1, Ordering::Relaxed);
36+
}
37+
pub fn exit_level() {
38+
LEVEL.fetch_sub(1, Ordering::Relaxed);
39+
}
40+
}
41+
42+
type VarId = usize;
43+
44+
#[derive(PartialEq, Hash, Eq)]
45+
enum TypeVar {
46+
Bound(BoundVar),
47+
Free(String),
48+
}
49+
50+
#[derive(PartialEq, Hash, Eq)]
51+
enum Scope {
52+
Bound(Box<Type>),
53+
Unbound(VarId),
54+
}
55+
56+
/// PolyType = `Forall [a b c]. a -> b -> c`
57+
struct PolyType {
58+
vars: Vec<TypeVar>,
59+
t: Box<Type>,
60+
}
61+
62+
impl PolyType {
63+
/// Takes a type with type vars inside and returns a polytype, with the type vars generalized inside the forall
64+
fn generalize(t: Type) -> PolyType {
65+
todo!()
66+
}
67+
/// Marks a type as not generalizable
68+
fn dont_generalize(t: Type) -> PolyType {
69+
todo!()
70+
}
71+
}
72+
73+
enum Expr {
74+
/// Identifier
75+
/// `x`
76+
Identifier(String),
77+
/// Lambda abstraction
78+
/// `\x -> e`
79+
Lambda(String, Box<Expr>),
80+
/// Function application
81+
/// `f e`
82+
Apply(Box<Expr>, Box<Expr>),
83+
/// Let binding
84+
/// `let f = e1 in e2`
85+
Let(String, Box<Expr>, Box<Expr>),
86+
}
87+
88+
impl Type {
89+
fn new_var() -> Type {
90+
let id = TYPEVAR_LEVEL.fetch_add(1, Ordering::Relaxed);
91+
Type::Var(Scope::Unbound(id))
92+
}
93+
94+
fn replace_vars(&self, table: &TypeTable) -> PolyType {
95+
match self {
96+
Type::Unit => PolyType::dont_generalize(Type::Unit),
97+
Type::Var(var) => match var {
98+
Scope::Bound(box t) => t.replace_vars(table),
99+
Scope::Unbound(id) => match table.lookup(id) {
100+
Some(t) => t.replace_vars(table),
101+
None => PolyType::dont_generalize(Type::new_var()),
102+
},
103+
},
104+
Type::Arrow(a, b) => {
105+
todo!()
106+
}
107+
};
108+
109+
todo!()
110+
}
111+
}
112+
113+
struct Env {
114+
bindings: HashMap<String, PolyType>,
115+
}
116+
117+
struct TypeTable {
118+
bindings: HashMap<VarId, Type>,
119+
}
120+
121+
impl TypeTable {
122+
fn lookup(&self, x: &VarId) -> Option<&Type> {
123+
self.bindings.get(x)
124+
}
125+
// Instantiates a polytype by replacing the type vars with fresh type vars
126+
fn inst(&mut self, type_: &PolyType) {
127+
todo!()
128+
}
129+
}
130+
131+
impl Env {
132+
fn lookup(&self, x: &str) -> Option<&PolyType> {
133+
self.bindings.get(x)
134+
}
135+
fn infer(&mut self, expr: Expr, type_table: &mut TypeTable) -> anyhow::Result<Type> {
136+
match expr {
137+
// Var rule
138+
// x : a ∈ Γ t = inst(a)
139+
// -----------------------
140+
// Γ ⊢ x : t
141+
Expr::Identifier(x) => {
142+
let a = self
143+
.lookup(&x)
144+
.ok_or_else(|| anyhow::anyhow!("Unbound variable"))?;
145+
let t = type_table.inst(a);
146+
}
147+
Expr::Lambda(_, _) => todo!(),
148+
Expr::Apply(_, _) => todo!(),
149+
Expr::Let(_, _, _) => todo!(),
150+
}
151+
todo!()
152+
}
153+
}

src/main.rs

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
fn main() {
2+
println!("Hello, world!");
3+
}

0 commit comments

Comments
 (0)