Skip to content

Commit 6938f22

Browse files
committed
Change to infer (un)roll, (un)wrap, and un(roll|wrap) in elim forms
Subtyping is changed to look for cases where one (but not both) of the types is a rolled type. Either a roll or unroll is then generated depending on which of the types `T1 <: T2` was a rolled type. Also, in case one of the types is wrapped, but the other is not, then a wrap or (pure) unwrap is generated. Impure unwraps are not implicitly generated. Also, elimination forms, like `if v then ... else ...` and `e.l`, try to (purely) unwrap and unroll, as appropriate, the type of their input. Together these changes largely eliminate the need to manually (un)roll and (un)wrap.
1 parent b66c48d commit 6938f22

File tree

17 files changed

+190
-126
lines changed

17 files changed

+190
-126
lines changed

README.md

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -341,14 +341,14 @@ This also shows that implicit functions naturally are first-class values.
341341
#### Recursive Types
342342

343343
There are no datatype definitions, recursive types have to be defined
344-
explicitly, and require explicit injection/projection.
344+
explicitly, and sometimes require explicit annotations.
345345

346346
```1ml
347347
type stream = rec t => {hd : int, tl : () ~> opt t} ;; creates rec type
348-
single x :@ stream = {hd = x, tl = fun () => none} ;; b :@ t rolls value into t
349-
({hd = n} :@ stream) = single 5 ;; p :@ t pattern matches on rec value
348+
single x : stream = {hd = x, tl = fun () => none} ;; b : t rolls value into t
349+
{hd = n} = single 5 ;; pattern match rec value
350350
do Int.print n ;; or:
351-
do Int.print (single 7 @: stream).hd ;; e @: t unrolls rec value directly
351+
do Int.print (single 7).hd ;; access rec value
352352
```
353353

354354
#### Recursive Functions
@@ -359,16 +359,16 @@ The `rec` expression form allows defining recursive functions:
359359
count = rec self => fun i =>
360360
if i <> 0 then self (i - 1)
361361
362-
repeat = rec self => fun x =>
363-
{hd = x, tl = fun () => some (self x)} :@ stream
362+
repeat = rec self => fun x : stream =>
363+
{hd = x, tl = fun () => some (self x)}
364364
```
365365

366366
Mutual recursion is also expressible:
367367

368368
```1ml
369369
{even, odd} = rec (self : {even : int ~> stream, odd : int ~> stream}) => {
370-
even x :@ stream = {hd = x, tl = fun () => some (self.odd (x + 1))}
371-
odd x :@ stream = {hd = x, tl = fun () => some (self.even (x + 1))}
370+
even x : stream = {hd = x, tl = fun () => some (self.odd (x + 1))}
371+
odd x : stream = {hd = x, tl = fun () => some (self.even (x + 1))}
372372
}
373373
```
374374

@@ -392,13 +392,15 @@ Opt :> OPT = {
392392
;; Church encoding; it requires the abstract type opt a to be implemented
393393
;; with a polymorphic (i.e., large) type. Thus, wrap the type.
394394
type opt a = wrap (b : type) -> b -> (a ~> b) ~> b
395-
none :# opt _ = fun (b : type) (n : b) (s : _ ~> b) => n
396-
some x :# opt _ = fun (b : type) (n : b) (s : _ ~> b) => s x
397-
caseopt (xo :# opt _) = xo _
395+
none (b : type) (n : b) (s : _ ~> b) = n
396+
some x (b : type) (n : b) (s : _ ~> b) = s x
397+
caseopt (xo : opt _) = xo _
398398
}
399399
```
400400

401-
Note how values of type `wrap T` have to be wrapped and unwrapped explicitly.
401+
Values of type `T` can be implicitly wrapped to `wrap T` and, in case `wrap T`
402+
contains no abstract types, can be implicitly unwrapped to `T`. In case `wrap T`
403+
contains abstract types, unwrapping must be done explicitly.
402404

403405
---
404406

elab.ml

Lines changed: 42 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -123,24 +123,40 @@ and paths_row ta ps = function
123123

124124

125125
let rec_from_extyp typ label s =
126-
match s with
127-
| ExT([], t) ->
128-
let rec find_rec = function
129-
| AppT(t, ts) ->
130-
let rec_t, unroll_t, roll_t, ak = find_rec t in
131-
rec_t, AppT(unroll_t, ts), AppT(roll_t, ts), ak
132-
| RecT(ak, unroll_t) as rec_t ->
133-
rec_t, unroll_t, rec_t, ak
134-
| DotT(t, lab) ->
135-
let rec_t, unroll_t, roll_t, ak = find_rec t in
136-
rec_t, DotT(unroll_t, lab), DotT(roll_t, lab), ak
137-
| _ ->
138-
error typ.at ("non-recursive type for " ^ label ^ ":"
139-
^ " " ^ Types.string_of_extyp s) in
140-
find_rec t
141-
| _ ->
126+
match try_rec_from_extyp s with
127+
| Some r -> r
128+
| None ->
142129
error typ.at ("non-recursive type for " ^ label ^ ":"
143-
^ " " ^ Types.string_of_extyp s)
130+
^ " " ^ string_of_extyp s)
131+
132+
133+
let try_unwrap (t, zs, e) =
134+
match t with
135+
| WrapT(ExT([], t)) -> Some (t, zs, IL.DotE(e, "wrap"))
136+
| _ -> None
137+
138+
let try_unroll (t, zs, e) =
139+
try_rec_from_typ t
140+
|> Lib.Option.map (fun (unroll_t, roll_t) ->
141+
unroll_t, zs, IL.UnrollE(e))
142+
143+
let try_peel r =
144+
try_unwrap r |> Lib.Option.orelse (fun () -> try_unroll r)
145+
146+
let avar fn r = fn r
147+
148+
let anexp fn r =
149+
match r with
150+
| ExT([], t), p, zs, e ->
151+
fn (t, zs, e)
152+
|> Lib.Option.map (fun (t, zs, e) ->
153+
ExT([], t), p, zs, e)
154+
| _ -> None
155+
156+
let rec fully fn pre r =
157+
match pre fn r with
158+
| None -> r
159+
| Some r -> fully fn pre r
144160

145161

146162
(* Instantiation *)
@@ -418,9 +434,8 @@ Trace.debug (lazy ("[FunE] env =" ^ VarSet.fold (fun a s -> s ^ " " ^ a) (domain
418434

419435
| EL.RollE(var, typ) ->
420436
let s, zs1 = elab_typ env typ l in
421-
let rec_t, unroll_t, roll_t, ak = rec_from_extyp typ "rolling" s in
437+
let unroll_t, roll_t = rec_from_extyp typ "rolling" s in
422438
let var_t = lookup_var env var in
423-
let unroll_t = subst_typ (subst [ak] [rec_t]) unroll_t in
424439
let _, zs2, f =
425440
try sub_typ env var_t unroll_t []
426441
with Sub e ->
@@ -432,7 +447,7 @@ Trace.debug (lazy ("[FunE] env =" ^ VarSet.fold (fun a s -> s ^ " " ^ a) (domain
432447
IL.RollE(IL.AppE(f, IL.VarE(var.it)), erase_typ roll_t)
433448

434449
| EL.IfE(var, exp1, exp2) ->
435-
let t0, zs0, ex = elab_instvar env var in
450+
let t0, zs0, ex = fully try_peel avar (elab_instvar env var) in
436451
let _ =
437452
match t0 with
438453
| PrimT(Prim.BoolT) -> ()
@@ -451,7 +466,8 @@ Trace.debug (lazy ("[FunE] env =" ^ VarSet.fold (fun a s -> s ^ " " ^ a) (domain
451466
IL.IfE(ex, IL.AppE(f1, e1), IL.AppE(f2, e2))
452467

453468
| EL.DotE(exp1, var) ->
454-
let ExT(aks, t), p, zs1, e1 = elab_instexp env exp1 "" in
469+
let ExT(aks, t), p, zs1, e1 =
470+
fully try_peel anexp (elab_instexp env exp1 "") in
455471
let tr, zs2 =
456472
match t with
457473
| StrT(tr) -> tr, []
@@ -476,7 +492,7 @@ Trace.debug (lazy ("[DotE] s = " ^ string_of_extyp s));
476492
IL.DotE(IL.VarE("x"), var.it), erase_extyp s))
477493

478494
| EL.AppE(var1, var2) ->
479-
let tf, zs1, ex1 = elab_instvar env var1 in
495+
let tf, zs1, ex1 = fully try_peel avar (elab_instvar env var1) in
480496
Trace.debug (lazy ("[AppE] tf = " ^ string_of_norm_typ tf));
481497
let aks1, t1, s, p, zs =
482498
match freshen_typ env tf with
@@ -509,7 +525,7 @@ Trace.debug (lazy ("[AppE] ts = " ^ String.concat ", " (List.map string_of_norm_
509525
let ExT(aks, t) as s2 = freshen_extyp env s2 in
510526
aks, t, s2, zs2
511527
| _ -> error typ.at "non-wrapped type for unwrap" in
512-
let t1, zs1, ex = elab_instvar env var in
528+
let t1, zs1, ex = fully try_unroll avar (elab_instvar env var) in
513529
let s1 =
514530
match t1 with
515531
| WrapT(s1) -> s1
@@ -526,14 +542,13 @@ Trace.debug (lazy ("[UnwrapE] s2 = " ^ string_of_norm_extyp s2));
526542

527543
| EL.UnrollE(var, typ) ->
528544
let s, zs1 = elab_typ env typ l in
529-
let rec_t, unroll_t, roll_t, ak = rec_from_extyp typ "unrolling" s in
545+
let unroll_t, roll_t = rec_from_extyp typ "unrolling" s in
530546
let var_t = lookup_var env var in
531547
let _, zs2, f = try sub_typ env var_t roll_t [] with Sub e ->
532548
error var.at ("unrolled value does not match annotation:"
533549
^ " " ^ Types.string_of_typ var_t ^ " "
534550
^ "<"
535551
^ " " ^ Types.string_of_typ roll_t) in
536-
let unroll_t = subst_typ (subst [ak] [rec_t]) unroll_t in
537552
ExT([], unroll_t), Pure, zs1 @ zs2,
538553
IL.UnrollE(IL.AppE(f, IL.VarE(var.it)))
539554

@@ -631,7 +646,8 @@ and elab_bind env bind l =
631646
erase_extyp s))
632647

633648
| EL.InclB(exp) ->
634-
let ExT(aks, t) as s, p, zs, e = elab_instexp env exp l in
649+
let ExT(aks, t) as s, p, zs, e =
650+
fully try_peel anexp (elab_instexp env exp l) in
635651
(match t with
636652
| StrT(tr) -> ()
637653
| InferT(z) -> resolve_always z (StrT[]) (* TODO: row polymorphism *)

examples/fc.1ml

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,13 @@ GADTs = {
1717
Succ : t int ~> case int
1818
Pair 'a 'b : t a ~> t b ~> case (a, b)
1919
}
20-
type T (type t _) a = (m: I t) ~> m.case a
21-
...rec {type t _} => {type t a = wrap T t a}
22-
T = T t
20+
...rec {type t _} => {type t a = wrap (m: I t) ~> m.case a}
2321
I = I t
24-
mk (fn: T _) = fn :# wrap T _ :@ t _
2522
t
26-
case (m: I) (e :# wrap T _ :@ t _) = e m
27-
Zero : t _ = mk fun (m: I) => m.Zero
28-
Succ x : t _ = mk fun (m: I) => m.Succ x
29-
Pair l r : t _ = mk fun (m: I) => m.Pair l r
23+
case (m: I) (e: t _) = e m
24+
Zero : t _ = fun (m: I) => m.Zero
25+
Succ x : t _ = fun (m: I) => m.Succ x
26+
Pair l r : t _ = fun (m: I) => m.Pair l r
3027
}
3128

3229
eval = rec (eval 'a: Exp.t a ~> a) =>

examples/hoas.1ml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,8 @@ let
1818
type T (type t _) x = (c: J t) ~> c.case x
1919
...{
2020
...rec {type t _} => {type t x = wrap T t x}
21-
case 'x (type case _) (cs: I case t) (e :# wrap T t x :@ t _) =
22-
e {case, ...cs}
23-
mk 'x (fn: T t x) = fn :# wrap T t x :@ t _
21+
case 'x (type case _) (cs: I case t) (e: t _) = e {case, ...cs}
22+
mk (fn: T t _) = fn
2423
} :> {
2524
type t _
2625
case 'x: (type case _) -> I case t -> t x ~> case x

examples/trie.1ml

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,14 @@ FunctionalTrie = {
88
alt 'v 'k1 'k2 : t k1 v ~> t k2 v ~> case (alt k1 k2) v
99
pair 'v 'k1 'k2 : t k1 (t k2 v) ~> case (k1, k2) v
1010
}
11-
type T (type t _ _) k v = (m: I t) ~> m.case k v
12-
...rec {type t _ _} => {type t k v = wrap T t k v}
13-
T = T t
11+
...rec {type t _ _} => {type t k v = wrap (m: I t) ~> m.case k v}
1412
I = I t
15-
mk (fn: T _ _) = fn :# wrap T _ _ :@ t _ _
1613

1714
t
18-
case (m: I) (e :# wrap T _ _ :@ t _ _) = e m
19-
unit vO : t _ _ = mk fun (m: I) => m.unit vO
20-
alt l r : t _ _ = mk fun (m: I) => m.alt l r
21-
pair lr : t _ _ = mk fun (m: I) => m.pair lr
15+
case (m: I) (e: t _ _) = e m
16+
unit vO : t _ _ = fun (m: I) => m.unit vO
17+
alt l r : t _ _ = fun (m: I) => m.alt l r
18+
pair lr : t _ _ = fun (m: I) => m.pair lr
2219

2320
lookup = rec (lookup 'k 'v: t k v ~> k ~> opt v) =>
2421
case {

import.ml

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,6 @@ let sig_ext = ".1mls"
99
let index_file = "index"
1010
let modules_dir = "node_modules"
1111

12-
let (<|>) xO uxO =
13-
match xO with
14-
| None -> uxO ()
15-
| some -> some
16-
1712
let finish path =
1813
if Lib.Sys.file_exists_at path
1914
|| Lib.Sys.file_exists_at (Lib.Filename.replace_ext mod_ext sig_ext path)
@@ -29,12 +24,13 @@ let complete path =
2924
finish (path ^ mod_ext)
3025

3126
let rec search_modules prefix suffix =
32-
complete (Filename.concat prefix modules_dir ^ "/" ^ suffix) <|> fun () ->
33-
let parent = Filename.dirname prefix in
34-
if parent = prefix then
35-
None
36-
else
37-
search_modules parent suffix
27+
complete (Filename.concat prefix modules_dir ^ "/" ^ suffix)
28+
|> Lib.Option.orelse (fun () ->
29+
let parent = Filename.dirname prefix in
30+
if parent = prefix then
31+
None
32+
else
33+
search_modules parent suffix)
3834

3935
let resolve parent path =
4036
let path = Lib.Filename.canonic path in
@@ -50,5 +46,6 @@ let resolve parent path =
5046
|| Lib.Sys.directory_exists_at parent then
5147
complete (Lib.Filename.canonic (Filename.dirname parent ^ "/" ^ path))
5248
else
53-
search_modules (Filename.dirname parent) path <|> fun () ->
54-
complete (std_dir ^ "/" ^ path)
49+
search_modules (Filename.dirname parent) path
50+
|> Lib.Option.orelse (fun () ->
51+
complete (std_dir ^ "/" ^ path))

lib.ml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,4 +113,8 @@ struct
113113
| [] -> Some ys
114114
| x::xs -> bind (xyO x) @@ fun y -> loop (y::ys) xs in
115115
loop [] xs |> map List.rev
116+
117+
let orelse alt = function
118+
| None -> alt ()
119+
| some -> some
116120
end

lib.mli

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,5 @@ sig
4343
val map: ('a -> 'b) -> 'a option -> 'b option
4444
val bind: 'a option -> ('a -> 'b option) -> 'b option
4545
val traverse: ('a -> 'b option) -> 'a list -> 'b list option
46+
val orelse: (unit -> 'a option) -> 'a option -> 'a option
4647
end

prelude/index.1ml

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,10 @@ Alt :> {
2626
inr 'a 'b: b -> t a b
2727
case 'a 'b 'o: {inl: a ~> o, inr: b ~> o} -> t a b ~> o
2828
} = {
29-
type t a b = wrap 'o -> {inl: a ~> o, inr: b ~> o} ~> o
30-
inl x :# t _ _ = fun c => c.inl x
31-
inr x :# t _ _ = fun c => c.inr x
32-
case c (x :# t _ _) = x c
29+
type t a b = wrap (type o) -> {inl: a ~> o, inr: b ~> o} ~> o
30+
inl x (type o) c : o = c.inl x
31+
inr x (type o) c : o = c.inr x
32+
case c (x : t _ _) = x _ c
3333
}
3434

3535
Alt = {
@@ -55,11 +55,10 @@ Pair = {
5555
}
5656

5757
List = {
58-
local ...Opt, ...Fun
5958
...rec {type t _} => {type t x = Opt.t (x, t x)}
60-
nil :@ t _ = none
61-
hd :: tl :@ t _ = some (hd, tl)
62-
case {nil, (::)} (x :@ t _) = x |> Opt.case {
59+
nil = Opt.none
60+
hd :: tl = Opt.some (hd, tl)
61+
case {nil, (::)} = Opt.case {
6362
none = nil
6463
some (hd, tl) = hd :: tl
6564
}

readme.1ml

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -47,25 +47,25 @@ p = f (fun x => x)
4747

4848

4949
type stream = rec t => {hd : int, tl : () ~> opt t} ;; creates rec type
50-
single x :@ stream = {hd = x, tl = fun () => none} ;; b :@ t rolls value into t
51-
({hd = n} :@ stream) = single 5 ;; p :@ t pattern matches on rec value
50+
single x : stream = {hd = x, tl = fun () => none} ;; b : t rolls value into t
51+
{hd = n} = single 5 ;; pattern match rec value
5252
do Int.print n ;; or:
53-
do Int.print (single 7 @: stream).hd ;; e @: t unrolls rec value directly
53+
do Int.print (single 7).hd ;; access rec value
5454

5555

5656

5757

5858
count = rec self => fun i =>
5959
if i <> 0 then self (i - 1)
6060

61-
repeat = rec self => fun x =>
62-
{hd = x, tl = fun () => some (self x)} :@ stream
61+
repeat = rec self => fun x : stream =>
62+
{hd = x, tl = fun () => some (self x)}
6363

6464

6565

6666
{even, odd} = rec (self : {even : int ~> stream, odd : int ~> stream}) => {
67-
even x :@ stream = {hd = x, tl = fun () => some (self.odd (x + 1))}
68-
odd x :@ stream = {hd = x, tl = fun () => some (self.even (x + 1))}
67+
even x : stream = {hd = x, tl = fun () => some (self.odd (x + 1))}
68+
odd x : stream = {hd = x, tl = fun () => some (self.even (x + 1))}
6969
}
7070

7171

@@ -80,7 +80,7 @@ Opt :> OPT = {
8080
;; Church encoding; it requires the abstract type opt a to be implemented
8181
;; with a polymorphic (i.e., large) type. Thus, wrap the type.
8282
type opt a = wrap (b : type) -> b -> (a ~> b) ~> b
83-
none :# opt _ = fun (b : type) (n : b) (s : _ ~> b) => n
84-
some x :# opt _ = fun (b : type) (n : b) (s : _ ~> b) => s x
85-
caseopt (xo :# opt _) = xo _
83+
none (b : type) (n : b) (s : _ ~> b) = n
84+
some x (b : type) (n : b) (s : _ ~> b) = s x
85+
caseopt (xo : opt _) = xo _
8686
}

0 commit comments

Comments
 (0)