Stefan pushed to branch master at Stefan / Typer
Commits:
402bdbe0 by Stefan Monnier at 2020-03-27T16:04:24-04:00
Infer type of `case` target from patterns
* src/elab.ml (check_case): Infer the type of the target from the
patterns when applicable.
* src/opslexp.ml (check''): Be careful to always pass the inferred type
and the expected type in the same order.
* src/unification.ml (create_metavar_1): New function extracted from
create_metavar.
(create_metavar): Use it.
(common_subset, s_offset): New functions.
(unify_metavar): Use them to properly handle the case of unifying
a metavar with itself where the two substitutions are different.
* btl/builtins.typer (Eq_comm): Simplify type annotation a bit.
* btl/pervasive.typer (List_head1, List_head, List_tail, List_concat)
(I, List_map-fst, List_map-snd): Remove unnecessary type annotation.
- - - - -
6 changed files:
- btl/builtins.typer
- btl/pervasive.typer
- samples/hott.typer
- src/elab.ml
- src/opslexp.ml
- src/unification.ml
Changes:
=====================================
btl/builtins.typer
=====================================
@@ -1,6 +1,6 @@
%%% builtins.typer --- Initialize the builtin functions
-%% Copyright (C) 2011-2018 Free Software Foundation, Inc.
+%% Copyright (C) 2011-2020 Free Software Foundation, Inc.
%%
%% Author: Pierre Delaunay <pierre.delaunay(a)hec.ca>
%% Keywords: languages, lisp, dependent types.
@@ -48,18 +48,18 @@ Eq_refl : ((x : ?t) ≡> Eq x x); % FIXME: `Eq ?x ?x` causes an error!
Eq_refl = Built-in "Eq.refl";
Eq_cast : (x : ?) ≡> (y : ?)
- ≡> (p : Eq x y)
+ ≡> Eq x y
≡> (f : ? -> ?)
≡> f x -> f y;
%% FIXME: I'd like to just say:
-%% Eq_cast : (p : Eq ?x ?y) ≡> ?f ?x -> ?f ?y;
+%% Eq_cast : Eq ?x ?y ≡> ?f ?x -> ?f ?y;
Eq_cast = Built-in "Eq.cast";
%% Commutativity of equality!
%% FIXME: I'd like to just say:
-%% Eq_comm : (p : Eq ?x ?y) -> Eq ?y ?x`;
-Eq_comm : (x : ?t) ≡> (y : ?t) ≡> (p : Eq x y) -> Eq (t := ?t) y x;
-Eq_comm p = Eq_cast (f := lambda xy -> Eq (t := t) xy x)
+%% Eq_comm : Eq ?x ?y -> Eq ?y ?x`;
+Eq_comm : (x : ?t) ≡> (y : ?t) ≡> Eq x y -> Eq y x;
+Eq_comm p = Eq_cast (f := lambda xy -> Eq xy x)
%% FIXME: I can't figure out how `(p := p)`
%% gets inferred here, yet it seems to work!?!
Eq_refl;
=====================================
btl/pervasive.typer
=====================================
@@ -1,6 +1,6 @@
%%% pervasive --- Always available definitions
-%% Copyright (C) 2011-2018 Free Software Foundation, Inc.
+%% Copyright (C) 2011-2020 Free Software Foundation, Inc.
%%
%% Author: Pierre Delaunay <pierre.delaunay(a)hec.ca>
%% Keywords: languages, lisp, dependent types.
@@ -38,7 +38,7 @@ none = datacons Option none;
%%%% List functions
-List_length : List ?a -> Int;
+List_length : List ?a -> Int; % Recursive defs aren't generalized :-(
List_length xs = case xs
| nil => 0
| cons hd tl =>
@@ -49,18 +49,15 @@ List_length xs = case xs
%% - Provide a default value : `a -> List a -> a`;
%% - Disallow problem case : `(l : List a) -> (l != nil) -> a`;
%% - Return an Option/Error
-List_head1 : List ?a -> Option ?a;
-List_head1 : (a : Type) ≡> List a -> Option a;
List_head1 xs = case xs
| nil => none
| cons hd tl => some hd;
-List_head x = lambda xs -> case (xs : List ?a)
- %% FIXME: We shouldn't need to annotate `xs` above!
+List_head x = lambda xs -> case xs
| cons x _ => x
| nil => x;
-List_tail xs = case (xs : List ?a) % FIXME: Same here.
+List_tail xs = case xs
| nil => nil
| cons hd tl => tl;
@@ -146,7 +143,6 @@ List_reverse l t = case l
| nil => t
| cons hd tl => List_reverse tl (cons hd t);
-List_concat : List ?a -> List ?a -> List ?a;
List_concat l t = List_reverse (List_reverse l nil) t;
List_foldl : (?a -> ?b -> ?a) -> ?a -> List ?b -> ?a;
@@ -183,7 +179,6 @@ List_empty xs = Int_eq (List_length xs) 0;
%%% Good 'ol combinators
-I : ?a -> ?a;
I x = x;
%% Use explicit erasable annotations since with an annotation like
@@ -429,15 +424,11 @@ List_merge xs ys = case xs
%% `Unmerge` a List of Pair
%% The two functions name said it all
-List_map-fst : List (Pair ?a ?b) -> List ?a;
List_map-fst xs = let
- mf : Pair ?a ?b -> ?a;
mf p = case p | pair x _ => x;
in List_map mf xs;
-List_map-snd : List (Pair ?a ?b) -> List ?b;
List_map-snd xs = let
- mf : Pair ?a ?b -> ?b;
mf p = case p | pair _ y => y;
in List_map mf xs;
=====================================
samples/hott.typer
=====================================
@@ -106,6 +106,9 @@ Equiv_function f (g : (x : ?A) -> ?B) = ((x : ?A) -> Eq (f x) (g x));
%% similarly restrict the univalent universes so they're always erased
%% before runtime (so we don't need to have the non-nop form of "cast"
%% at run-time).
+%% This can be thought of as adding a new equality to some universes,
+%% aka making a "quotient universe", which, like HIT, requires proving
+%% that every time we use types from this universe we obey those equalities.
%%%% Propositions, resizing, etc...
=====================================
src/elab.ml
=====================================
@@ -240,7 +240,7 @@ let ctx_define_rec (ctx: elab_context) decls =
* infer the types and perform macro-expansion.
*
* More specifically, we do it with 2 mutually recursive functions:
- * - `check` takes a Pexp along with its expected type and return an Lexp
+ * - `check` takes a Pexp along with its expected type and returns an Lexp
* of that type (hopefully)
* - `infer` takes a Pexp and infers its type (which it returns along with
* the Lexp).
@@ -251,8 +251,8 @@ let ctx_define_rec (ctx: elab_context) decls =
* metavars we create/instantiate/dereference as well as the number of call to
* the unification algorithm.
* Basically guessing/annotations is only needed at those few places where the
- * code is not fully-normalized, which in normal programs is only in "let"
- * definitions.
+ * code is not fully-normalized (which in normal programs is only in "let"
+ * definitions) as well as when we fill implicit arguments.
*)
let newMetavar (ctx : lexp_context) sl name t =
@@ -462,8 +462,8 @@ let rec elaborate ctx se ot =
* elaborate ctx (Node (Symbol (l, "typer-funcall"), func::args)) ot
* but that forces `typer-funcall` to elaborate `func` a second time!
* Maybe I should only elaborate `func` above if it's a symbol
- * (and maybe even use `elaborate_varref` rather than indirecting
- * through `typr-identifier`)? *)
+ * (and maybe even use `elab_varref` rather than indirecting
+ * through `typer-identifier`)? *)
elab_call ctx ft args
and infer (p : sexp) (ctx : elab_context): lexp * ltype =
@@ -626,24 +626,49 @@ and check_case rtype (loc, target, ppatterns) ctx =
(* get target and its type *)
let tlxp, tltp = infer target ctx in
- (* FIXME: We need to be careful with whnf: while the output is "equivalent"
- * to the input, it's not necessarily as readable/efficient.
- * So try to reuse the "non-whnf" form whenever possible. *)
- let call_split e = match (OL.lexp_whnf e (ectx_to_lctx ctx)) with
- | Call (f, args) -> (f, args)
- | _ -> (e,[]) in
- let it, targs = call_split tltp in
- let constructors = match OL.lexp_whnf it (ectx_to_lctx ctx) with
- (* FIXME: Check that it's `Inductive' only after performing Unif.unify
- * with the various branches, so that we can infer the type
- * of the target from the type of the patterns. *)
- | Inductive (_, _, fargs, constructors)
- -> assert (List.length fargs = List.length targs);
- constructors
- | _ -> lexp_error (sexp_location target) tlxp
- ("Can't `case` on objects of this type: "
- ^ lexp_string tltp);
- SMap.empty in
+ let it_cs_as = ref None in
+ let ltarget = ref tlxp in
+
+ let get_cs_as it' lctor =
+ match !it_cs_as with
+ | Some (it, cs, args)
+ -> let _ = match Unif.unify it' it (ectx_to_lctx ctx) with
+ | (_::_)
+ -> lexp_error loc lctor
+ ("Expected pattern of type `"
+ ^ lexp_string it ^ "` but got `"
+ ^ lexp_string it' ^ "`")
+ | [] -> () in
+ (cs, args)
+ | None
+ -> match OL.lexp_whnf it' (ectx_to_lctx ctx) with
+ | Inductive (_, _, fargs, constructors)
+ -> let (s, targs) = List.fold_left
+ (fun (s, targs) (ak, name, t)
+ -> let arg = newMetavar
+ (ectx_to_lctx ctx)
+ (ectx_to_scope_level ctx)
+ name (mkSusp t s) in
+ (S.cons arg s, (ak, arg) :: targs))
+ (S.identity, [])
+ fargs in
+ let (cs, args) = (constructors, List.rev targs) in
+ ltarget := check_inferred ctx tlxp tltp (mkCall (it', args));
+ it_cs_as := Some (it', cs, args);
+ (cs, args)
+ | _ -> let call_split e = match (OL.lexp_whnf e (ectx_to_lctx ctx))
+ with | Call (f, args) -> (f, args)
+ | _ -> (e,[]) in
+ let (it, targs) = call_split tltp in
+ let constructors = match OL.lexp_whnf it (ectx_to_lctx ctx) with
+ | Inductive (_, _, fargs, constructors)
+ -> assert (List.length fargs = List.length targs);
+ constructors
+ | _ -> lexp_error (sexp_location target) tlxp
+ ("Can't `case` on objects of this type: "
+ ^ lexp_string tltp);
+ SMap.empty in
+ (constructors, targs) in
(* Read patterns one by one *)
let fold_fun (lbranches, dflt) (pat, pexp) =
@@ -669,19 +694,13 @@ and check_case rtype (loc, target, ppatterns) ctx =
| e -> e in
match nosusp (inst_args ctx lctor) with
| Cons (it', (_, cons_name))
- -> let _ = match Unif.unify it' it (ectx_to_lctx ctx) with
- | (_::_)
- -> lexp_error loc lctor
- ("Expected pattern of type `"
- ^ lexp_string it ^ "` but got `"
- ^ lexp_string it' ^ "`")
- | [] -> () in
- let _ = check_uniqueness pat cons_name lbranches in
+ -> let _ = check_uniqueness pat cons_name lbranches in
+ let (constructors, targs) = get_cs_as it' lctor in
let cargs
= try SMap.find cons_name constructors
with Not_found
-> lexp_error loc lctor
- ("`" ^ (lexp_string it)
+ ("`" ^ (lexp_string it')
^ "` does not have a `"
^ cons_name ^ "` constructor");
[] in
@@ -972,10 +991,16 @@ and lexp_decls_macro (loc, mname) sargs ctx: sexp =
with e ->
fatal ~loc ("Macro `" ^ mname ^ "` not found")
+(* Elaborate a bunch of mutually-recursive definitions.
+ * FIXME: Currently, we never apply generalization to recursive definitions,
+ * which means we can't define `List_length` or `List_map` without adding
+ * explicit type annotations :-( *)
and lexp_check_decls (ectx : elab_context) (* External context. *)
(nctx : elab_context) (* Context with type declarations. *)
(defs : (symbol * sexp) list)
: (vname * lexp * ltype) list * elab_context =
+ (* FIXME: Generalize when/where possible, so things like `map` can be
+ defined without type annotations! *)
(* Preserve the new operators added to nctx. *)
let ectx = let (_, a, b, c) = ectx in
let (grm, _, _, _) = nctx in
=====================================
src/opslexp.ml
=====================================
@@ -486,11 +486,11 @@ let rec check'' erased ctx e =
let _ = List.fold_left (fun n (v, e, t)
-> assert_type
nctx e
- (push_susp t (S.shift n))
(check (if DB.set_mem (n - 1) nerased
then DB.set_empty
else nerased)
- nctx e);
+ nctx e)
+ (push_susp t (S.shift n));
n - 1)
(List.length defs) defs in
mkSusp (check nerased nctx e)
@@ -530,7 +530,7 @@ let rec check'' erased ctx e =
(error_tc ~loc:(lexp_location arg)
"arg kind mismatch"; ())
else ();
- assert_type ctx arg t1 at;
+ assert_type ctx arg at t1;
mkSusp t2 (S.substitute arg)
| _ -> (error_tc ~loc:(lexp_location arg)
("Calling a non function (type = "
@@ -620,8 +620,8 @@ let rec check'' erased ctx e =
(erased, ctx)) in
let (nerased, nctx) = mkctx erased ctx s vdefs fieldtypes in
assert_type nctx branch
- (mkSusp ret (S.shift (List.length fieldtypes)))
- (check nerased nctx branch))
+ (check nerased nctx branch)
+ (mkSusp ret (S.shift (List.length fieldtypes))))
branches;
let diff = SMap.cardinal constructors - SMap.cardinal branches in
(match default with
@@ -629,8 +629,8 @@ let rec check'' erased ctx e =
-> if diff <= 0 then
warning_tc ~loc:l "Redundant default clause";
let nctx = (DB.lctx_extend ctx v (LetDef (0, e)) etype) in
- assert_type nctx d (mkSusp ret (S.shift 1))
- (check (DB.set_sink 1 erased) nctx d)
+ assert_type nctx d (check (DB.set_sink 1 erased) nctx d)
+ (mkSusp ret (S.shift 1))
| None
-> if diff > 0 then
error_tc ~loc:l ("Non-exhaustive match: "
=====================================
src/unification.ml
=====================================
@@ -32,13 +32,16 @@ let log_info = Log.log_info ~section:"UNIF"
(* :-( *)
let global_last_metavar = ref (-1) (*The first metavar is 0*)
-let create_metavar (ctx : DB.lexp_context) (sl : scope_level) (t : ltype)
+let create_metavar_1 (sl : scope_level) (t : ltype) (clen : int)
= let idx = !global_last_metavar + 1 in
global_last_metavar := idx;
- metavar_table := U.IMap.add idx (MVar (sl, t, Myers.length ctx))
+ metavar_table := U.IMap.add idx (MVar (sl, t, clen))
(!metavar_table);
idx
+let create_metavar (ctx : DB.lexp_context) (sl : scope_level) (t : ltype)
+ = create_metavar_1 sl t (Myers.length ctx)
+
(* For convenience *)
type constraint_kind =
| CKimpossible (* Unification is simply impossible. *)
@@ -110,8 +113,60 @@ let occurs_in (id: meta_id) (e : lexp) : bool = match metavar_lookup id with
var after all! *)
(metavar_table := old_mvt; true)
else false
-
-(****************************** Top level unify *************************************)
+
+(* When unifying a metavar with itself, if the two metavars don't
+ * have the same substitution applied, then we should take the "common subset"
+ * of those two substitutions: e.g.
+ *
+ * ?a[0 => x, 1 => y, 2 => z, ...] =?= ?a[0 => x, 1 => b, 2 => z, ...]
+ *
+ * then we know `?a` can't use variable #1 since if it did then the two
+ * substitutions would result in different terms.
+ * We can do that by instantiating `?a` with a new metavar:
+ *
+ * ?a = ?b[0 => 0, 1 => 2, ...]
+ * aka
+ * ?a = ?b[0 => 0 · ↑1]
+ *)
+let common_subset ctx s1 s2 =
+ let rec loop s1 s2 o1 o2 o =
+ match (s1, s2) with
+ | (S.Cons (le1, s1', o1'), S.Cons (le2, s2', o2'))
+ -> let o1 = o1 + o1' in
+ let o2 = o2 + o2' in
+ (* FIXME: We should check if le1 and le2 are *unifiable* instead! *)
+ if not (le1 = impossible || le1 = impossible)
+ && OL.conv_p ctx (mkSusp le1 (S.shift o1)) (mkSusp le2 (S.shift o2))
+ then match loop s1' s2' o1 o2 1 with
+ | S.Identity 1 -> S.Identity o (* Optimization! *)
+ | s' -> S.Cons (mkVar ((lexp_location le1, None), 0),
+ s', o)
+ else loop s1' s2' o1 o2 (o + 1)
+ (* If one of them reached `Identity`, unroll it, knowing that
+ *
+ * Identity 0 = #0 · #1 · #2 ... = #0 · (Identity 1)
+ *)
+ | (S.Cons _, S.Identity o2')
+ -> loop s1 (S.Cons (mkVar ((U.dummy_location, None), 0),
+ S.Identity 1, o2'))
+ o1 o2 o
+ | (S.Identity o1', S.Cons _)
+ -> loop (S.Cons (mkVar ((U.dummy_location, None), 0),
+ S.Identity 1, o1'))
+ s2 o1 o2 o
+ | (S.Identity o1', S.Identity o2')
+ -> assert (o1 + o1' = o2 + o2');
+ S.Identity o
+ in loop s1 s2 0 0 0
+
+(* Return the number of vars difference between input and output context. *
+ * Could be returned directly by `common_subset`, but it's pretty easy to
+ * compute it here instead. *)
+let rec s_offset s = match s with
+ | S.Identity o -> o
+ | S.Cons (_, s', o) -> o - 1 + s_offset s'
+
+(************************** Top level unify **********************************)
(** Dispatch to the right unifier.
@@ -161,7 +216,7 @@ and unify' (e1: lexp) (e2: lexp)
else ((* print_string "Unification failure on default\n"; *)
[(CKresidual, ctx, e1, e2)]))
-(********************************* Type specific unify *******************************)
+(************************* Type specific unify *******************************)
(** Unify a Arrow and a lexp if possible
- (Arrow, Arrow) -> if var_kind = var_kind
@@ -218,7 +273,7 @@ and unify_lambda (lambda: lexp) (lxp: lexp) ctx vs : return_type =
- metavar , metavar -> if Metavar = Metavar then OK else ERROR
- metavar , lexp -> OK
*)
-and unify_metavar ctx idx s (lxp1: lexp) (lxp2: lexp)
+and unify_metavar ctx idx s1 (lxp1: lexp) (lxp2: lexp)
: return_type =
let unif idx s lxp =
let t = match metavar_lookup idx with
@@ -246,17 +301,68 @@ and unify_metavar ctx idx s (lxp1: lexp) (lxp2: lexp)
^ "\n" ^ "for " ^ lexp_string lxp ^ "\n");
[(CKresidual, ctx, lxp1, lxp2)] in
match lxp2 with
- | Metavar (idx2, s2, _)
+ | Metavar (idx2, s2, name)
-> if idx = idx2 then
- (* FIXME: handle the case where s1 != s2 !! *)
- []
+ match common_subset ctx s1 s2 with
+ | S.Identity 0 -> [] (* Optimization! *)
+ (* ¡ s1 != s2 !
+ * Create a new metavar that can only refer to those vars
+ * which are mapped identically by `s1` and `s2`
+ * (as found by `common_subset`).
+ * This metavar doesn't necessarily live exactly in `ctx`
+ * nor even a proper prefix of it, tho :-( !!
+ *)
+ | s ->
+ (* print_string "Metavar idx-idx s1!=s2\n"; *)
+ assert (not (OL.conv_p ctx lxp1 lxp2));
+ match (Inverse_subst.inverse s,
+ metavar_lookup idx) with
+ | (None, _)
+ -> Log.internal_error
+ "Can't inverse subst returned by common_subset!"
+ | (_, MVal _)
+ -> Log.internal_error
+ "`lexp_whnf` returned an instantiated metavar!!"
+ | (Some s_inv, MVar (sl, t, clen))
+ -> let clen' = clen - s_offset s in
+ let t' = mkSusp t s_inv in
+ let newmv = create_metavar_1 sl t' clen' in
+ let lexp = mkMetavar (newmv, s, name) in
+ assert (sl <= clen);
+ assert (sl <= clen');
+ assert (OL.conv_p ctx (mkSusp t' s) t);
+ (* if (OL.conv_p ctx (mkSusp lexp s1) (mkSusp lexp s2)) then
+ * print_string ("common_subset successful:\n "
+ * ^ subst_string s
+ * ^ "\n =\n "
+ * ^ subst_string s1
+ * ^ "\n ∩\n "
+ * ^ subst_string s2
+ * ^ "\n")
+ * else
+ * print_string ("common_subset failed:\n "
+ * ^ subst_string s
+ * ^ "\n ∘\n "
+ * ^ subst_string s1
+ * ^ "\n =\n "
+ * ^ subst_string (scompose s s1)
+ * ^ "\n!=\n "
+ * ^ subst_string s
+ * ^ "\n ∘\n "
+ * ^ subst_string s2
+ * ^ "\n =\n "
+ * ^ subst_string (scompose s s2)
+ * ^ "\n"); *)
+ metavar_table := associate idx lexp (!metavar_table);
+ assert (OL.conv_p ctx lxp1 lxp2);
+ []
else
(* If one of the two subst can't be inverted, try the other.
* FIXME: There's probably a more general solution. *)
- (match unif idx s lxp2 with
+ (match unif idx s1 lxp2 with
| [] -> []
| _ -> unif idx2 s2 lxp1)
- | _ -> unif idx s lxp2
+ | _ -> unif idx s1 lxp2
(** Unify a Call (call) and a lexp (lxp)
- Call , Call -> UNIFY
@@ -359,7 +465,7 @@ and unify_sort (sort_: lexp) (lxp: lexp) ctx vs : return_type =
| Sort _, Var _ -> [(CKresidual, ctx, sort_, lxp)]
| _, _ -> [(CKimpossible, ctx, sort_, lxp)]
-(************************ Helper function **************************************)
+(************************ Helper function ************************************)
(***** for Case *****)
(** Check arg_king in <code>(arg_kind * vdef option) list </code> in Case *)
View it on GitLab: https://gitlab.com/monnier/typer/-/commit/402bdbe074bfa0149141759cb37733846…
--
View it on GitLab: https://gitlab.com/monnier/typer/-/commit/402bdbe074bfa0149141759cb37733846…
You're receiving this email because of your account on gitlab.com.
Stefan pushed to branch master at Stefan / Typer
Commits:
c081c586 by Stefan Monnier at 2020-03-20T11:28:31-04:00
-
- - - - -
1 changed file:
- samples/hott.typer
Changes:
=====================================
samples/hott.typer
=====================================
@@ -47,8 +47,8 @@
%% We additionally need some kind of elimination on paths like:
%%
%% Eq_call : Eq(_ := ?t) ?x ?y ≡> I ≡> ?t;
-%% Eq_call _ i₀ ↝ ?x
-%% Eq_call _ i₁ ↝ ?y
+%% Eq_call (_ : Eq x y) i₀ ↝ x
+%% Eq_call (_ : Eq x y) i₁ ↝ y
%%
%% Heterogenous equality could look like:
%%
@@ -81,7 +81,7 @@ Equiv_function f (g : (x : ?A) -> ?B) = ((x : ?A) -> Eq (f x) (g x));
%%
%% It'd be great to support univalence without losing `Eq_cast`, but
%% it's not at all clear how:
-%% - One way is to make it a non-axiom and replace it with a system that
+%% - One way is to make it a non-axiom and implement it as a system that
%% proves it directly on a case-by-case basis, as in the paper
%% "Equivalence for Free!".
%% - Another is to make `Eq_cast` take a proof of `isSet T`,
@@ -95,6 +95,17 @@ Equiv_function f (g : (x : ?A) -> ?B) = ((x : ?A) -> Eq (f x) (g x));
%%
%% but that begs the question: how could we prevent "promoting"
%% a non-erasable proof to an erasable one?
+%% - Another is to distinguish univalent universes, as done in the paper
+%% "Extending Homotopy Type Theory with Strict Equality".
+%% E.g. Our sorts `Type ℓ` would be refined to `Type k ℓ` where `k`
+%% would be a boolean indicating if the universe admits univalence or not,
+%% and then the J (aka "cast") rule would be restricted so that a proof
+%% about equality between univalent types can only be used to coerce
+%% between univalent types.
+%% This is reminiscent of the confinement of Prop in Coq, so we could
+%% similarly restrict the univalent universes so they're always erased
+%% before runtime (so we don't need to have the non-nop form of "cast"
+%% at run-time).
%%%% Propositions, resizing, etc...
View it on GitLab: https://gitlab.com/monnier/typer/-/commit/c081c5869f90f7f00b87a1ac179557bb1…
--
View it on GitLab: https://gitlab.com/monnier/typer/-/commit/c081c5869f90f7f00b87a1ac179557bb1…
You're receiving this email because of your account on gitlab.com.
Stefan pushed to branch master at Stefan / Typer
Commits:
b4bacaf0 by Stefan Monnier at 2020-03-17T14:26:41-04:00
* src/opslexp.ml (impredicative_erase): Disable it
(impredicative_universe_poly): Rename from deep_universe_poly.
- - - - -
1 changed file:
- src/opslexp.ml
Changes:
=====================================
src/opslexp.ml
=====================================
@@ -1,6 +1,6 @@
(* opslexp.ml --- Operations on Lexps
-Copyright (C) 2011-2019 Free Software Foundation, Inc.
+Copyright (C) 2011-2020 Free Software Foundation, Inc.
Author: Stefan Monnier <monnier(a)iro.umontreal.ca>
Keywords: languages, lisp, dependent types.
@@ -46,11 +46,13 @@ let conv_erase = true (* Makes conv ignore erased terms. *)
(* `impredicative_erase` is inconsistent: as shown in samples/hurkens.typer
* it allows the definition of an inf-looping expression of type ⊥. *)
-let impredicative_erase = true (* Allows erasable args to be impredicative. *)
+let impredicative_erase = false (* Allows erasable args to be impredicative. *)
-(* The safety of `deep_universe_poly` is unknown.
- * But I also like the idea. *)
-let deep_universe_poly = true (* Assume arg is TypeLevel.z when erasable. *)
+(* The safety of `impredicative_universe_poly` is unknown.
+ * But I also like the idea.
+ * Furthermore it is sufficient to be able to encode System-F (tho
+ * I haven't been able to generalize this result to Fω). *)
+let impredicative_universe_poly = true (* Assume arg is TypeLevel.z when erasable. *)
(* Lexp context *)
@@ -360,7 +362,7 @@ let sort_compose ctx1 ctx2 l ak k1 k2 =
else let l2' = (mkSusp l2 (S.substitute impossible)) in
SortResult (mkSort (l, Stype (mkSLlub ctx1 l1 l2')))
| (StypeLevel, Stype l2)
- when ak == P.Aerasable && deep_universe_poly
+ when ak == P.Aerasable && impredicative_universe_poly
(* The safety/soundness of this rule is completely unknown.
* It's pretty powerful, e.g. allows tuples containing
* level-polymorphic functions, and makes impredicative-encoding
View it on GitLab: https://gitlab.com/monnier/typer/-/commit/b4bacaf063bc0fbdd54347c6f9e8b9068…
--
View it on GitLab: https://gitlab.com/monnier/typer/-/commit/b4bacaf063bc0fbdd54347c6f9e8b9068…
You're receiving this email because of your account on gitlab.com.
Stefan pushed to branch master at Stefan / Typer
Commits:
93c051db by Stefan Monnier at 2020-03-17T14:06:01-04:00
* samples/hott.typer: New file
- - - - -
1 changed file:
- + samples/hott.typer
Changes:
=====================================
samples/hott.typer
=====================================
@@ -0,0 +1,132 @@
+%%% HoTT -- Homotopy Type-Theory
+
+%% Copyright (C) 2020 Free Software Foundation, Inc.
+%%
+%% Author: Stefan Monnier <monnier(a)iro.umontreal.ca>
+%%
+%% This file is part of Typer.
+%%
+%% Typer is free software; you can redistribute it and/or modify it under the
+%% terms of the GNU General Public License as published by the Free Software
+%% Foundation, either version 3 of the License, or (at your option) any
+%% later version.
+%%
+%% Typer is distributed in the hope that it will be useful, but WITHOUT ANY
+%% WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+%% FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+%% more details.
+%%
+%% You should have received a copy of the GNU General Public License along
+%% with this program. If not, see <http://www.gnu.org/licenses/>.
+
+%%% Commentary:
+
+%% Various definitions inspired from the HoTT book.
+
+%%% Code:
+
+%%%% Paths
+
+%% Cubical interval type `I`
+%% Path type:
+%%
+%% I : Type0?
+%% i₀ : I;
+%% i₁ : I;
+%%
+%% Note: while one cannot eliminate (no `if`) on `I`, one can use
+%% `∨`, `∧`, and `¬` on values of this type, and maybe one could do
+%% `type I | i₀ | i₁` and allow elimination with the simple restriction
+%% that you can only eliminate an `I` to another `I` (so one could define
+%% `∨` by hand). Better yet, maybe instead of a special `I` we could simply
+%% use a normal `Bool`, since the `≡>` already prevents elimination?
+%%
+%% Eq : (t : Type) ≡> t → t → Type;
+%% path : (p : I ≡> t) → Eq (p(_ := i₀)) (p(_ := i₁));
+%%
+%% We additionally need some kind of elimination on paths like:
+%%
+%% Eq_call : Eq(_ := ?t) ?x ?y ≡> I ≡> ?t;
+%% Eq_call _ i₀ ↝ ?x
+%% Eq_call _ i₁ ↝ ?y
+%%
+%% Heterogenous equality could look like:
+%%
+%% HEq (t₁ : Type) ≡> (t₂ : Type) ≡> t₁ → t₂ → Type;
+%% type HEq x₁ x₂
+%% | Hrefl (p : Eq t₁ t₂) (Eq (coe p x₁) x₂);
+%% type HEq (t : (i : I) ≡> Type)
+%% (x₁ : t(i := i₀)) (x₂ : t(i := i₁))
+%% | Hrefl (p : Eq t₁ t₂) (Eq (coe p x₁) x₂);
+
+%%%% Univalence
+
+%% type Equiv_function (f : ?A -> ?B) (g : ?A -> ?B)
+%% | equiv_function ((x : ?) -> Eq (f x) (g x));
+Equiv_function f (g : (x : ?A) -> ?B) = ((x : ?A) -> Eq (f x) (g x));
+%% Equiv_function_axiom : Equiv_function ?f ?g -> Eq ?f ?g;
+
+%% type HoTT_IsEquiv (f : ?A -> ?B)
+%% | hott_isequiv (Equiv_function (compose f ?g) identity)
+%% (Equiv_function (compose ?h f) identity);
+
+%% type Equiv_type (A : Type) (B : Type)
+%% | equiv_type (f : A -> B) (HoTT_Isequiv f);
+%% univalence_axiom : Equiv_type A B -> Eq A B;
+
+%% FIXME: Univalence is incompatible with Typer's `Eq_cast` because
+%% it allows casting between, say, `Nat` and `BinNat` which is not
+%% a no-op. Cubical Agda supports it by making its "Eq elimination" into a
+%% non-trivial operation whose `Eq` proof is very much non-erasable.
+%%
+%% It'd be great to support univalence without losing `Eq_cast`, but
+%% it's not at all clear how:
+%% - One way is to make it a non-axiom and replace it with a system that
+%% proves it directly on a case-by-case basis, as in the paper
+%% "Equivalence for Free!".
+%% - Another is to make `Eq_cast` take a proof of `isSet T`,
+%% but that prevents use of `Eq_cast` between `Nat` and `α`
+%% since `isSet Type` is not true.
+%% It would also prevent use of `Eq_cast` on HIT.
+%% - Ideally another would be to require `Eq_cast` to take an additional
+%% proof that the equality proof is equal to `eq_refl` or more generally:
+%%
+%% Eq_cast: (eq: Eq ?x ?y) ≡> Eq_erasable eq ≡> ....
+%%
+%% but that begs the question: how could we prevent "promoting"
+%% a non-erasable proof to an erasable one?
+
+%%%% Propositions, resizing, etc...
+
+HoTT_isSet A = (x : A) -> (y : A) -> (p : Eq x y) -> (q : Eq x y) -> Eq p q;
+%% `isProp` basically implies proof irrelevance.
+%% So it also implies erasability. Note that if we use the double-negation
+%% encoding of classical `or` in type-theory, then it preserves `isProp`!
+HoTT_isProp P = (x : P) -> (y : P) -> Eq x y;
+
+%% Provable without axioms:
+%%
+%% ¬¬¬A -> ¬A
+
+Inverse_double_negation : ?A -> Not (Not ?A);
+Inverse_double_negation a na = na a;
+
+Weak_double_negation : Not (Not (Not ?A)) -> Not ?A;
+Weak_double_negation nnna a = nnna (lambda na -> na a);
+
+%% BEWARE: univalence_axiom incompatible with general LEM!
+%% HoTT_LEM_axiom : HoTT_isProp A -> ¬¬A -> A;
+
+%% "mere propositions" can be treated impredicatively!!
+%% HoTT_propositional_resizing_axiom :
+%% Equiv_type { A : Type l | isProp A} { A : Type (l + 1) | isProp A}
+
+%% Propositional truncation: ||A|| is equivalent to A but is a mere proposition.
+%% One way to approximate could be:
+Propositional_truncation A = (P : ?) ≡> HoTT_isProp P ≡> (A -> P) -> P;
+propositional_truncation : ?A -> Propositional_truncation ?A;
+propositional_truncation a f = f a;
+
+
+
+%%% hott.typer ends here.
View it on GitLab: https://gitlab.com/monnier/typer/-/commit/93c051dbed31e0ccc2969fea85ca5d25f…
--
View it on GitLab: https://gitlab.com/monnier/typer/-/commit/93c051dbed31e0ccc2969fea85ca5d25f…
You're receiving this email because of your account on gitlab.com.