Skip to content

Commit b12f200

Browse files
committed
self-variable notation for record types, close #64
1 parent 87d3b3a commit b12f200

File tree

6 files changed

+125
-42
lines changed

6 files changed

+125
-42
lines changed

docs/source/codata-types.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,20 @@ That is, we use brackets and bars instead of parentheses and commas. Moreover,
3232
It is often helpful to think of a codatatype as akin to an *interface* in an object-oriented programming language, in which case the variable ``x`` is like the ``this`` or ``self`` pointer by which an object refers to itself. Of course an interface in a simply-typed language does not need a self-pointer to specify the *types* of its methods, but in a dependently typed language it does. In higher-dimensional type theories, the presence of this variable can be used in other ways than simply accessing previously declared methods, such as in the definition of semi-simplicial types using :ref:`Displayed coinductive types`.
3333

3434

35+
Self variables for record types
36+
-------------------------------
37+
38+
The syntax of self variables is also available as an alternative when defining record types. For instance, the usual sort of Σ-type, as a record with eta-conversion, can also be defined by
39+
40+
.. code-block:: none
41+
42+
def Σ (A : Type) (B : A → Type) : Type ≔ sig (
43+
x .fst : A,
44+
x .snd : B (x .fst))
45+
46+
It should be emphasized that this is just a different notation for record types, not a "codatatype with eta-conversion". At present there is no practical difference, but in the future recursion will be forbidden in record types, even those that use the self-variable notation.
47+
48+
3549
Copattern matching
3650
------------------
3751

lib/core/check.ml

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -648,11 +648,37 @@ let rec check : type a b s.
648648
| Eq -> None
649649
| Neq -> Some ()))
650650
None fields in
651-
check_codata status ctx tyargs Emp
651+
check_codata status ctx Noeta tyargs Emp
652652
(Fibrancy.Codata.empty dim dim (Ctx.dbwd ctx) Noeta
653653
(readback_neu ctx (head_of_potential head) apps))
654654
(Bwd.to_list fields) Emp ~has_higher_fields)
655655
| _ -> fatal (Checking_canonical_at_nonuniverse ("codatatype", PVal (ctx, ty))))
656+
| SelfRecord fields, Potential (head, apps, _) -> (
657+
match view_type ~severity ty "typechecking self-record" with
658+
| Canonical (_, UU dim, ins, tyargs) -> (
659+
match (D.compare_zero dim, Endpoints.hott (), !gel_ok) with
660+
| Pos _, Some _, false ->
661+
fatal (Unimplemented "general higher-dimensional types in HOTT: use glue")
662+
| _ -> (
663+
let Eq = eq_of_ins_zero ins in
664+
let has_higher_fields =
665+
Bwd.fold_left
666+
(fun acc (Field.Wrap fld, _) ->
667+
match acc with
668+
| Some () -> Some ()
669+
| None -> (
670+
match D.compare (Field.dim fld) D.zero with
671+
| Eq -> None
672+
| Neq -> Some ()))
673+
None fields in
674+
match has_higher_fields with
675+
| Some () -> fatal (Unimplemented "higher fields in record types: use codata")
676+
| None ->
677+
check_codata status ctx Eta tyargs Emp
678+
(Fibrancy.Codata.empty dim dim (Ctx.dbwd ctx) Eta
679+
(readback_neu ctx (head_of_potential head) apps))
680+
(Bwd.to_list fields) Emp ~has_higher_fields))
681+
| _ -> fatal (Checking_canonical_at_nonuniverse ("codatatype", PVal (ctx, ty))))
656682
| Record (xs, fields, opacity), Potential (head, apps, _) -> (
657683
match view_type ~severity ty "typechecking record" with
658684
| Canonical (_, UU dim, ins, tyargs) -> (
@@ -679,6 +705,7 @@ let rec check : type a b s.
679705
| Synth (Match _), Kinetic l -> kinetic_of_potential l ctx tm ty "match"
680706
| Refute _, Kinetic l -> kinetic_of_potential l ctx tm ty "match"
681707
| Codata _, Kinetic l -> kinetic_of_potential l ctx tm ty "codata"
708+
| SelfRecord _, Kinetic l -> kinetic_of_potential l ctx tm ty "sig"
682709
| Record _, Kinetic l -> kinetic_of_potential l ctx tm ty "sig"
683710
| Data _, Kinetic l -> kinetic_of_potential l ctx tm ty "data"
684711
(* If the user left a hole, we create an eternal metavariable. *)
@@ -1926,33 +1953,34 @@ and with_codata_so_far : type a b n c et.
19261953
in
19271954
run_with_definition h (hyp codataterm) errs @@ fun () -> cont domvars codataterm
19281955

1929-
and check_codata : type a b n.
1956+
and check_codata : type a b n et.
19301957
(b, potential) status ->
19311958
(a, b) Ctx.t ->
1959+
(potential, et) eta ->
19321960
(D.zero, n, n, normal) TubeOf.t ->
1933-
(b * n * no_eta) Term.CodatafieldAbwd.t ->
1934-
(n, n, b, no_eta) Fibrancy.Codata.t ->
1961+
(b * n * et) Term.CodatafieldAbwd.t ->
1962+
(n, n, b, et) Fibrancy.Codata.t ->
19351963
(Field.wrapped * a Raw.codatafield) list ->
19361964
Code.t Asai.Diagnostic.t Bwd.t ->
19371965
has_higher_fields:unit option ->
19381966
(b, potential) term =
1939-
fun status ctx tyargs checked_fields fibrancy raw_fields errs ~has_higher_fields ->
1967+
fun status ctx eta tyargs checked_fields fibrancy raw_fields errs ~has_higher_fields ->
19401968
let dim = TubeOf.inst tyargs in
19411969
match raw_fields with
19421970
| [] -> (
19431971
match errs with
19441972
| Emp ->
1945-
with_codata_so_far status Noeta ctx `Opaque dim tyargs checked_fields fibrancy
1973+
with_codata_so_far status eta ctx `Opaque dim tyargs checked_fields fibrancy
19461974
~has_higher_fields errs
19471975
@@ fun _ codataterm -> codataterm
19481976
| Snoc _ -> fatal (Accumulated ("check_codata", errs)))
19491977
| (Wrap fld, Codatafield (x, rty)) :: raw_fields -> (
1950-
with_codata_so_far status Noeta ctx `Opaque dim tyargs checked_fields fibrancy
1978+
with_codata_so_far status eta ctx `Opaque dim tyargs checked_fields fibrancy
19511979
~has_higher_fields errs
19521980
@@ fun domvars _ ->
19531981
let newctx = Ctx.cube_vis ctx x domvars in
1954-
match (D.compare_zero (Field.dim fld), D.compare_zero (TubeOf.inst tyargs)) with
1955-
| Zero, _ ->
1982+
match (D.compare_zero (Field.dim fld), D.compare_zero (TubeOf.inst tyargs), eta) with
1983+
| Zero, _, _ ->
19561984
(* Zero-dimensional field *)
19571985
let checked_fields, fibrancy, errs =
19581986
Reporter.try_with ~fatal:(fun e -> (checked_fields, fibrancy, Snoc (errs, e)))
@@ -1961,16 +1989,19 @@ and check_codata : type a b n.
19611989
let cty = check (Kinetic `Nolet) newctx rty (universe D.zero) in
19621990
let entry = CodatafieldAbwd.Entry (fld, Lower cty) in
19631991
(Snoc (checked_fields, entry), Fibrancy.Codata.add_field fibrancy entry, errs) in
1964-
check_codata status ctx tyargs checked_fields fibrancy raw_fields errs ~has_higher_fields
1965-
| Pos _, Zero ->
1992+
check_codata status ctx eta tyargs checked_fields fibrancy raw_fields errs
1993+
~has_higher_fields
1994+
| Pos _, Zero, Noeta ->
19661995
(* Higher-dimensional field, currently requires a zero-dimensional codatatype (non-Gel). *)
19671996
let checked_fields, errs =
19681997
Reporter.try_with ~fatal:(fun e -> (checked_fields, Snoc (errs, e))) @@ fun () ->
19691998
let (Degctx (plusmap, degctx, _)) = degctx newctx (Field.dim fld) in
19701999
let cty = check (Kinetic `Nolet) degctx rty (universe D.zero) in
19712000
(Snoc (checked_fields, Entry (fld, Codatafield.Higher (plusmap, cty))), errs) in
1972-
check_codata status ctx tyargs checked_fields fibrancy raw_fields errs ~has_higher_fields
1973-
| Pos _, Pos _ -> fatal (Unimplemented "higher fields in higher-dimensional codatatypes"))
2001+
check_codata status ctx eta tyargs checked_fields fibrancy raw_fields errs
2002+
~has_higher_fields
2003+
| Pos _, Zero, Eta -> fatal (Unimplemented "higher fields in record types")
2004+
| Pos _, Pos _, _ -> fatal (Unimplemented "higher fields in higher-dimensional codatatypes"))
19742005

19752006
and check_record : type a f1 f2 f af d acd b n.
19762007
(b, potential) status ->

lib/core/dump.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,7 @@ module F = struct
308308
| Data _ -> fprintf ppf "Data(?)"
309309
| Codata _ -> fprintf ppf "Codata(?)"
310310
| Record (_, _, _) -> fprintf ppf "Record(?)"
311+
| SelfRecord _ -> fprintf ppf "SelfRecord(?)"
311312
| Refute (_, _) -> fprintf ppf "Refute(?)"
312313
| Hole _ -> fprintf ppf "Hole"
313314
| Realize x -> fprintf ppf "Realize %a" check x

lib/core/raw.ml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ module rec Make : functor (I : Indices) -> sig
129129
| Data : (Constr.t, 'a dataconstr located) Abwd.t -> 'a check
130130
| Codata : (Field.wrapped, 'a codatafield) Abwd.t -> 'a check
131131
| Record : ('a, 'c, 'ac) Namevec.t located * ('ac, 'd, 'acd) tel * opacity -> 'a check
132+
| SelfRecord : (Field.wrapped, 'a codatafield) Abwd.t -> 'a check
132133
| Refute : 'a synth located list * [ `Explicit | `Implicit ] -> 'a check
133134
| Hole : {
134135
scope : 'a I.scope;
@@ -302,6 +303,8 @@ functor
302303
| Codata : (Field.wrapped, 'a codatafield) Abwd.t -> 'a check
303304
(* A record type binds its "self" variable namelessly, exposing it to the user by additional variables that are bound locally to its fields. This can't be "cubeified" as easily, so we have the user specify a list of ordinary variables to be its boundary. Thus, in practice below 'c must be a number of faces associated to a dimension, but the parser doesn't know the dimension, so it can't ensure that. The unnamed internal variable is included as the last one. *)
304305
| Record : ('a, 'c, 'ac) Namevec.t located * ('ac, 'd, 'acd) tel * opacity -> 'a check
306+
(* There's also a notation for record types that uses self variables like codata. *)
307+
| SelfRecord : (Field.wrapped, 'a codatafield) Abwd.t -> 'a check
305308
(* Empty match against the first one of the arguments belonging to an empty type. *)
306309
| Refute : 'a synth located list * [ `Explicit | `Implicit ] -> 'a check
307310
(* A hole must store the entire "state" from when it was entered, so that the user can later go back and fill it with a term that would have been valid in its original position. This includes the variables in lexical scope, which are available only during parsing, so we store them here at that point. During typechecking, when the actual metavariable is created, we save the lexical scope along with its other context and type data. A hole also stores its source location so that proofgeneral can create an overlay at that place, and the notation tightnesses of the hole location. *)
@@ -528,6 +531,12 @@ module Resolve (R : Resolver) = struct
528531
(fun (R.T1.Codatafield (x, fld)) ->
529532
R.T2.Codatafield (R.rename ctx x, check (R.snoc ctx x) fld))
530533
fields)
534+
| SelfRecord fields ->
535+
SelfRecord
536+
(Abwd.map
537+
(fun (R.T1.Codatafield (x, fld)) ->
538+
R.T2.Codatafield (R.rename ctx x, check (R.snoc ctx x) fld))
539+
fields)
531540
| Record (xs, fields, opaq) ->
532541
let (Bplus ac2) = R.T2.bplus (R.T1.Namevec.length xs.value) in
533542
let xs2 = renames ctx xs.value ac2 in

lib/core/reporter.ml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1076,6 +1076,12 @@ let extra_field_in_struct : type s et. (s, et) eta -> string * string list -> Co
10761076
| Eta -> Extra_field_in_tuple (Some (fst fld))
10771077
| Noeta -> Extra_method_in_comatch fld
10781078

1079+
let duplicate_field_in_type : type s et i. (s, et) eta -> i Field.t -> Code.t =
1080+
fun eta fld ->
1081+
match eta with
1082+
| Eta -> Duplicate_field_in_record fld
1083+
| Noeta -> Duplicate_method_in_codata fld
1084+
10791085
let duplicate_field_in_struct : type s et. (s, et) eta -> string * string list -> Code.t =
10801086
fun eta fld ->
10811087
match eta with

lib/parser/builtins.ml

Lines changed: 51 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1960,6 +1960,37 @@ let rec codata_fields bar_ok =
19601960
(terms [ (Op "|", Lazy (lazy (codata_fields false))); (RBracket, Done_closed codata) ]));
19611961
}
19621962

1963+
let process_codata_field : type n lt ls rt rs lt' ls' rt' rs' et.
1964+
(potential, et) eta ->
1965+
(Field.wrapped, n Raw.codatafield) Abwd.t ->
1966+
(string option, n) Bwv.t ->
1967+
(lt, ls, rt, rs) parse located ->
1968+
(lt', ls', rt', rs') parse located ->
1969+
Field.wrapped * n Raw.codatafield =
1970+
fun eta flds ctx tm ty ->
1971+
match tm.value with
1972+
| App
1973+
{ fn = { value = x; loc = xloc }; arg = { value = Field (fstr, fdstr, _); loc = fldloc }; _ }
1974+
-> (
1975+
with_loc tm.loc @@ fun () ->
1976+
if not (Lexer.valid_field fstr) then fatal ?loc:fldloc (Invalid_field fstr);
1977+
let x =
1978+
match x with
1979+
| Ident ([ x ], _) when Lexer.valid_var x -> Some x
1980+
| Placeholder _ -> None
1981+
| Ident (x, _) -> fatal ?loc:xloc (Invalid_variable x)
1982+
| _ -> fatal ?loc:xloc Parse_error in
1983+
match dim_of_string (String.concat "" fdstr) with
1984+
| Some (Any fdim) -> (
1985+
let fld = Field.intern fstr fdim in
1986+
match Abwd.find_opt (Field.Wrap fld) flds with
1987+
| Some _ -> fatal ?loc:fldloc (duplicate_field_in_type eta fld)
1988+
| None ->
1989+
let ty = process (Bwv.snoc ctx x) ty in
1990+
(Field.Wrap fld, Raw.Codatafield (x, ty)))
1991+
| None -> fatal (Invalid_field (String.concat "." ("" :: fstr :: fdstr))))
1992+
| _ -> fatal ?loc:tm.loc Parse_error
1993+
19631994
let rec process_codata : type n.
19641995
(Field.wrapped, n Raw.codatafield) Abwd.t ->
19651996
(string option, n) Bwv.t ->
@@ -1969,34 +2000,8 @@ let rec process_codata : type n.
19692000
fun flds ctx obs loc ->
19702001
match obs with
19712002
| [ Token (RBracket, _) ] -> { value = Raw.Codata flds; loc }
1972-
| Token (Op "|", _) :: Term tm :: Token (Colon, _) :: Term ty :: obs -> (
1973-
match tm.value with
1974-
| App
1975-
{
1976-
fn = { value = x; loc = xloc };
1977-
arg = { value = Field (fstr, fdstr, _); loc = fldloc };
1978-
_;
1979-
} -> (
1980-
with_loc tm.loc @@ fun () ->
1981-
if not (Lexer.valid_field fstr) then fatal ?loc:fldloc (Invalid_field fstr);
1982-
let x =
1983-
match x with
1984-
| Ident ([ x ], _) when Lexer.valid_var x -> Some x
1985-
| Placeholder _ -> None
1986-
| Ident (x, _) -> fatal ?loc:xloc (Invalid_variable x)
1987-
| _ -> fatal ?loc:xloc Parse_error in
1988-
match dim_of_string (String.concat "" fdstr) with
1989-
| Some (Any fdim) -> (
1990-
let fld = Field.intern fstr fdim in
1991-
match Abwd.find_opt (Field.Wrap fld) flds with
1992-
| Some _ -> fatal ?loc:fldloc (Duplicate_method_in_codata fld)
1993-
| None ->
1994-
let ty = process (Bwv.snoc ctx x) ty in
1995-
process_codata
1996-
(Abwd.add (Field.Wrap fld) (Raw.Codatafield (x, ty)) flds)
1997-
ctx obs loc)
1998-
| None -> fatal (Invalid_field (String.concat "." ("" :: fstr :: fdstr))))
1999-
| _ -> fatal ?loc:tm.loc Parse_error)
2003+
| Token (Op "|", _) :: Term tm :: Token (Colon, _) :: Term ty :: obs ->
2004+
process_codata (Snoc (flds, process_codata_field Noeta flds ctx tm ty)) ctx obs loc
20002005
| _ -> invalid "codata 1"
20012006

20022007
let rec pp_codata_fields first prews accum obs : document * Whitespace.t list =
@@ -2104,8 +2109,23 @@ let rec process_tel : type a.
21042109
let (Any_tel tel) = process_tel ctx (StringSet.add name seen) obs in
21052110
Any_tel (Ext (Some name, ty, tel)))
21062111
else fatal ?loc (Invalid_field name)
2112+
| Term { loc; _ } :: Token (Colon, _) :: Term _ :: _ -> fatal ?loc Parse_error
21072113
| _ -> invalid "record"
21082114

2115+
let rec process_self_record : type n.
2116+
(Field.wrapped, n Raw.codatafield) Abwd.t ->
2117+
(string option, n) Bwv.t ->
2118+
observation list ->
2119+
Asai.Range.t option ->
2120+
n check located =
2121+
fun flds ctx obs loc ->
2122+
match obs with
2123+
| [ Token (RParen, _) ] -> { value = Raw.SelfRecord flds; loc }
2124+
| Token (Op ",", _) :: obs -> process_self_record flds ctx obs loc
2125+
| Term tm :: Token (Colon, _) :: Term ty :: obs ->
2126+
process_self_record (Snoc (flds, process_codata_field Eta flds ctx tm ty)) ctx obs loc
2127+
| _ -> invalid "self record"
2128+
21092129
let process_record ctx obs loc =
21102130
let opacity, obs =
21112131
match obs with
@@ -2137,10 +2157,12 @@ let process_record ctx obs loc =
21372157
let ctx = Bwv.append ac ctx vars in
21382158
let (Any_tel tel) = process_tel ctx StringSet.empty obs in
21392159
Range.locate (Raw.Record (locate_opt x.loc (namevec_of_vec ac vars), tel, opacity)) loc
2160+
| Token (LParen, _) :: (Term { value = App _; _ } :: _ as obs) ->
2161+
process_self_record Emp ctx obs loc
21402162
| Token (LParen, _) :: obs ->
21412163
let ctx = Bwv.snoc ctx None in
21422164
let (Any_tel tel) = process_tel ctx StringSet.empty obs in
2143-
{ value = Record ({ value = [ None ]; loc }, tel, opacity); loc }
2165+
Range.locate (Raw.Record ({ value = [ None ]; loc }, tel, opacity)) loc
21442166
| _ -> invalid "record"
21452167

21462168
let rec pp_record_fields prews accum obs =

0 commit comments

Comments
 (0)