ok fuck rescript
This commit is contained in:
parent
2d31adff52
commit
f85b5d6785
10 changed files with 98 additions and 52 deletions
6
.gitignore
vendored
6
.gitignore
vendored
|
@ -2,7 +2,11 @@
|
||||||
*.pdf
|
*.pdf
|
||||||
|
|
||||||
_build
|
_build
|
||||||
|
*.bs.mjs
|
||||||
*.bs.js
|
*.bs.js
|
||||||
|
|
||||||
node_modules
|
node_modules
|
||||||
lib/bs
|
lib/bs
|
||||||
|
|
||||||
|
__tests__/**/*.mjs
|
||||||
|
src/**/*.mjs
|
17
__tests__/terms_test.res
Normal file
17
__tests__/terms_test.res
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
open Jest
|
||||||
|
open Belt
|
||||||
|
open Data
|
||||||
|
open Bidir
|
||||||
|
|
||||||
|
open Expect
|
||||||
|
open! Expect.Operators
|
||||||
|
|
||||||
|
let mapFirst = a => a->Result.map(((x, _)) => x)
|
||||||
|
|
||||||
|
test("id", () => {
|
||||||
|
let id: term = Lam("x", Var("x"))
|
||||||
|
let ctx = list{}
|
||||||
|
let typ = synthesize(ctx, id)->mapFirst
|
||||||
|
Js.Console.error2("output: ", typ)
|
||||||
|
expect(typ) === Ok(TPolytype("ex0", TArrow(TVar("ex0"), TVar("ex0"))))
|
||||||
|
})
|
9
babel.config.js
Normal file
9
babel.config.js
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
module.exports = {
|
||||||
|
presets: [
|
||||||
|
[
|
||||||
|
"@babel/preset-env",
|
||||||
|
{ targets: { node: "current", esmodules: false }, modules: "cjs" },
|
||||||
|
],
|
||||||
|
],
|
||||||
|
plugins: [],
|
||||||
|
};
|
3
bidir-writeup.typ
Normal file
3
bidir-writeup.typ
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
= Expressions
|
||||||
|
|
||||||
|
- $ id : forall a. a arrow.r a $
|
|
@ -1,17 +1,17 @@
|
||||||
{
|
{
|
||||||
"name": "your-project-name",
|
"name": "your-project-name",
|
||||||
|
"bsc-flags": [],
|
||||||
"sources": [
|
"sources": [
|
||||||
{
|
{ "dir": "src", "subdirs": true },
|
||||||
"dir": "src",
|
{ "dir": "__tests__", "subdirs": true, "type": "dev" }
|
||||||
"subdirs": true
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
"package-specs": [
|
"package-specs": [
|
||||||
{
|
{
|
||||||
"module": "es6",
|
"module": "commonjs",
|
||||||
"in-source": true
|
"in-source": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"suffix": ".bs.js",
|
"suffix": ".bs.js",
|
||||||
"bs-dependencies": []
|
"bs-dependencies": [],
|
||||||
|
"bs-dev-dependencies": ["@glennsl/bs-jest"]
|
||||||
}
|
}
|
||||||
|
|
BIN
bun.lockb
BIN
bun.lockb
Binary file not shown.
10
jest.config.json
Normal file
10
jest.config.json
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"verbose": true,
|
||||||
|
"moduleFileExtensions": ["js", "mjs"],
|
||||||
|
"extensionsToTreatAsEsm": [".bs.mjs"],
|
||||||
|
"testMatch": ["**/__tests__/**/*_test.mjs", "**/__tests__/**/*_test.bs.js"],
|
||||||
|
"transform": {
|
||||||
|
"^.+.m?js$": "babel-jest"
|
||||||
|
},
|
||||||
|
"transformIgnorePatterns": ["node_modules/(?!(rescript)/)"]
|
||||||
|
}
|
|
@ -1,9 +1,14 @@
|
||||||
{
|
{
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"res:build": "rescript",
|
"res:build": "rescript",
|
||||||
"res:dev": "rescript build -w"
|
"res:dev": "rescript build -w",
|
||||||
|
"test": "retest lib/**/*.mjs"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"rescript": "^10.1.4"
|
"@glennsl/bs-jest": "^0.7.0",
|
||||||
|
"jest": "^29.7.0",
|
||||||
|
"rescript": "^10.1.4",
|
||||||
|
"rescript-js": "^1.0.0-beta.2",
|
||||||
|
"rescript-test": "^5.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ let _genSymCtr = ref(0)
|
||||||
let genSym = (~prefix="var", ()): string => {
|
let genSym = (~prefix="var", ()): string => {
|
||||||
let num = _genSymCtr.contents
|
let num = _genSymCtr.contents
|
||||||
_genSymCtr := num + 1
|
_genSymCtr := num + 1
|
||||||
`${prefix}${Int.toString(num)}`
|
`${prefix}${num->Int.toString}`
|
||||||
}
|
}
|
||||||
|
|
||||||
let rec isSubtype = (ctx: context, tyA: typ, tyB: typ): result<context, string> => {
|
let rec isSubtype = (ctx: context, tyA: typ, tyB: typ): result<context, string> => {
|
||||||
|
@ -17,31 +17,30 @@ let rec isSubtype = (ctx: context, tyA: typ, tyB: typ): result<context, string>
|
||||||
and typecheck = (ctx: context, term: term, typ: typ): result<context, string> => {
|
and typecheck = (ctx: context, term: term, typ: typ): result<context, string> => {
|
||||||
switch (term, typ) {
|
switch (term, typ) {
|
||||||
// 1I rule
|
// 1I rule
|
||||||
| (Unit, Unit) => Ok(ctx)
|
| (Unit, TUnit) => Ok(ctx)
|
||||||
|
|
||||||
// ∀I rule
|
// ∀I rule
|
||||||
| (e, Polytype(x, tyA)) => failwith("TODO")
|
| (e, TPolytype(x, tyA)) => failwith("TODO ∀I-rule")
|
||||||
|
|
||||||
// →I rule
|
// →I rule
|
||||||
| (Lam(x, e), Arrow(tyA, tyB)) =>
|
| (Lam(x, e), TArrow(tyA, tyB)) =>
|
||||||
let augmentedCtx = ctx->List.add(TermAnnot(x, tyA))
|
let augmentedCtx = ctx->List.add(TermAnnot(x, tyA))
|
||||||
typecheck(augmentedCtx, e, tyB)->Result.map(_ => ctx)
|
typecheck(augmentedCtx, e, tyB)->Result.map(_ => ctx)
|
||||||
|
|
||||||
// Sub rule
|
// Sub rule
|
||||||
// TODO: Subtyping rule???
|
| (_, _) =>
|
||||||
|
let tyA = synthesize(ctx, term)
|
||||||
| (Var(_) | Lam(_, _) | App(_, _) | Annot(_, _), _) => failwith("TODO")
|
let tyA' = applyContext(ctx, tyA)
|
||||||
| _ => Error("could not resolve")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
and synthesize = (ctx: context, term: term): result<(typ, context), string> => {
|
and synthesize = (ctx: context, term: term): result<(typ, context), string> => {
|
||||||
switch term {
|
switch term {
|
||||||
// Var rule
|
// Var rule
|
||||||
| Var(name) => Ok(lookupTypeVariable(ctx, name), ctx)
|
| Var(name) => lookupTypeVariable(ctx, name)->Result.map(ty => (ty, ctx))
|
||||||
|
|
||||||
// 1I⇒ rule
|
// 1I⇒ rule
|
||||||
| Unit => Ok(Unit, ctx)
|
| Unit => Ok(TUnit, ctx)
|
||||||
|
|
||||||
// Anno rule
|
// Anno rule
|
||||||
| Annot(e, tyA) => typecheck(ctx, e, tyA)->Result.map(ctx => (tyA, ctx))
|
| Annot(e, tyA) => typecheck(ctx, e, tyA)->Result.map(ctx => (tyA, ctx))
|
||||||
|
@ -51,35 +50,39 @@ and synthesize = (ctx: context, term: term): result<(typ, context), string> => {
|
||||||
// TODO: Check x and e
|
// TODO: Check x and e
|
||||||
let exA = genSym(~prefix="ex", ())
|
let exA = genSym(~prefix="ex", ())
|
||||||
let exB = genSym(~prefix="ex", ())
|
let exB = genSym(~prefix="ex", ())
|
||||||
Ok(Arrow(Existential(exA), Existential(exB)), ctx)
|
let augmentedCtx = list{TermAnnot(x, TVar(exA)), TypeVar(exB), TypeVar(exA), ...ctx}
|
||||||
|
typecheck(augmentedCtx, e, TVar(exB))->Result.map(_ => (
|
||||||
|
TArrow(TExistential(exA), TExistential(exB)),
|
||||||
|
ctx,
|
||||||
|
))
|
||||||
|
|
||||||
// →E rule
|
// →E rule
|
||||||
| App(e1, e2) => failwith("TODO")
|
| App(e1, e2) => synthesize(ctx, e1)->Result.flatMap(((tyA, ctx)) => appSynthesize(ctx, tyA, e2))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
and appSynthesize = (ctx: context, funTy: typ, term: term): result<(typ, context), string> => {
|
and appSynthesize = (ctx: context, funTy: typ, term: term): result<(typ, context), string> => {
|
||||||
switch (funTy, term) {
|
switch (funTy, term) {
|
||||||
// →App rule
|
// →App rule
|
||||||
| (Arrow(tyA, tyC), e) => typecheck(ctx, e, tyA)->Result.map(_ => (tyC, ctx))
|
| (TArrow(tyA, tyC), e) => typecheck(ctx, e, tyA)->Result.map(_ => (tyC, ctx))
|
||||||
|
|
||||||
// ∀App rule
|
// ∀App rule
|
||||||
| (Polytype(a, tyA), e) => failwith("TODO")
|
| (TPolytype(a, tyA), e) => failwith("TODO")
|
||||||
|
|
||||||
// âApp rule
|
// âApp rule
|
||||||
| (Existential(_), _) => failwith("TODO")
|
| (TExistential(_), _) => failwith("TODO")
|
||||||
|
|
||||||
| (Unit | Var(_), _) => Error("trying to appSynthesize with a non-function type")
|
| (TUnit | TVar(_), _) => Error("trying to appSynthesize with a non-function type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Figure 8. Applying a context, as a substitution, to a type
|
// Figure 8. Applying a context, as a substitution, to a type
|
||||||
let rec applyContext = (ctx: context, typ: typ): typ => {
|
and applyContext = (ctx: context, typ: typ): typ => {
|
||||||
switch typ {
|
switch typ {
|
||||||
| Unit => Unit
|
| TUnit => TUnit
|
||||||
| Var(x) => Var(x)
|
| TVar(x) => TVar(x)
|
||||||
| Existential(_) => failwith("TODO")
|
| TExistential(_) => failwith("TODO")
|
||||||
| Polytype(_) => failwith("TODO")
|
| TPolytype(_) => failwith("TODO")
|
||||||
| Arrow(a, b) => Arrow(applyContext(ctx, a), applyContext(ctx, b))
|
| TArrow(a, b) => TArrow(applyContext(ctx, a), applyContext(ctx, b))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
37
src/data.res
37
src/data.res
|
@ -1,6 +1,3 @@
|
||||||
// Disable warning about the same constructor for different types
|
|
||||||
@@warning("-30")
|
|
||||||
|
|
||||||
type rec term =
|
type rec term =
|
||||||
| Unit // ()
|
| Unit // ()
|
||||||
| Var(string) // x
|
| Var(string) // x
|
||||||
|
@ -9,18 +6,18 @@ type rec term =
|
||||||
| Annot(term, typ) // (e : A)
|
| Annot(term, typ) // (e : A)
|
||||||
|
|
||||||
and typ =
|
and typ =
|
||||||
| Unit // 1
|
| TUnit // 1
|
||||||
| Var(string) // α
|
| TVar(string) // α
|
||||||
| Existential(string) // â
|
| TExistential(string) // â
|
||||||
| Polytype(string, typ) // ∀α.A
|
| TPolytype(string, typ) // ∀α.A
|
||||||
| Arrow(typ, typ) // A -> B
|
| TArrow(typ, typ) // A -> B
|
||||||
|
|
||||||
and monotyp =
|
and monotyp =
|
||||||
// Unit monotype
|
// Unit monotype
|
||||||
| Unit // 1
|
| MUnit // 1
|
||||||
| Var(string) // α
|
| MVar(string) // α
|
||||||
| Existential(string) // â
|
| MExistential(string) // â
|
||||||
| Arrow(monotyp, monotyp) // τ -> σ
|
| MArrow(monotyp, monotyp) // τ -> σ
|
||||||
|
|
||||||
type rec contextEntry =
|
type rec contextEntry =
|
||||||
| TypeVar(string) // Γ,α
|
| TypeVar(string) // Γ,α
|
||||||
|
@ -30,20 +27,18 @@ type rec contextEntry =
|
||||||
| Marker(string) // Γ,🢒â
|
| Marker(string) // Γ,🢒â
|
||||||
|
|
||||||
type rec completeContextEntry =
|
type rec completeContextEntry =
|
||||||
| TypeVar(string) // Ω,α
|
| CCTypeVar(string) // Ω,α
|
||||||
| TermAnnot(string, typ) // Ω,x:A
|
| CCTermAnnot(string, typ) // Ω,x:A
|
||||||
| ExistentialSolved(string, monotyp) // Ω,â=τ
|
| CCExistentialSolved(string, monotyp) // Ω,â=τ
|
||||||
| Marker(string) // Ω,🢒â
|
| CCMarker(string) // Ω,🢒â
|
||||||
|
|
||||||
type context = list<contextEntry>
|
type context = list<contextEntry>
|
||||||
|
|
||||||
@@warning("+30")
|
|
||||||
|
|
||||||
/** Context lookup */
|
/** Context lookup */
|
||||||
let rec lookupTypeVariable = (ctx: list<contextEntry>, name: string): typ => {
|
let rec lookupTypeVariable = (ctx: list<contextEntry>, name: string): result<typ, string> => {
|
||||||
switch ctx {
|
switch ctx {
|
||||||
| list{} => failwith("L")
|
| list{} => Error(`could not find name ${name}`)
|
||||||
| list{TermAnnot(n, t), ..._} if n == name => t
|
| list{TermAnnot(n, t), ..._} if n == name => Ok(t)
|
||||||
| list{_, ...rest} => lookupTypeVariable(rest, name)
|
| list{_, ...rest} => lookupTypeVariable(rest, name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue