Template-Coq: Obtaining constructors of a type

Tags: / coq /

A description of what I did when I first tried to obtain the constructors of a Coq Inductive type using Template-Coq but without using TemplateMonad.

Quite a crude way. There got to be better and elegant ways. This post is sort of a note to my future self.

TemplateMonad seems to be the right way to do this, but in this post it is not used. Maybe it'll help appreciate the utility of TemplateMonad.

Versions of software used:


I had an example inductive type:

Inductive C :=
| r : C
| g : nat -> C
| b : bool -> nat -> C.

And I needed to find a way to extract the constructors of this type as an AST (abstract syntax tree).

Template-Coq, which is part of the MetaCoq project, allows us to do this.

So the aim is to get a result like

[("r", astof(C));
 ("g", astof(nat -> C));
 ("b", astof(nat -> C))]

Template-Coq can do:

We're gonna need the quoting part.

Let's start. First, we 'quote' C recursively (the non-recursive quoting doesn't give as much level of detail) and store its AST form in qC:

From MetaCoq.Template Require Import utils All.
Require Import List String.
Import ListNotations MonadNotation.

MetaCoq Quote Recursively Definition qC := C.

qC looks like

([(MPfile ["myfilename"], "C",
  InductiveDecl
    {|
    ind_finite := Finite;   (* Recursivity kind: Inductive. Not co-inductive *)
    ind_npars := 0;         (* No parameters *)
    ind_params := [];       (* No parameters *)
    ind_bodies := [{|
                   ind_name := "C";
                   ind_type := tSort
                                 (Universe.from_kernel_repr
                                    (Level.lSet, false) []);
                   ind_kelim := InType;
                   ind_ctors := [("r", tRel 0, 0);
                                ("g",
                                tProd nAnon
                                  (tInd
                                     {|
                                     inductive_mind := (
                                                 MPfile
                                                 ["Datatypes"; "Init"; "Coq"],
                                                 "nat");
                                     inductive_ind := 0 |} []) 
                                  (tRel 1), 1);
                                ("b",
                                tProd nAnon
                                  (tInd
                                     {|
                                     inductive_mind := (
                                                 MPfile
                                                 ["Datatypes"; "Init"; "Coq"],
                                                 "bool");
                                     inductive_ind := 0 |} [])
                                  (tProd nAnon
                                     (tInd
                                        {|
                                        inductive_mind := (
                                                 MPfile
                                                 ["Datatypes"; "Init"; "Coq"],
                                                 "nat");
                                        inductive_ind := 0 |} []) 
                                     (tRel 2)), 2)];
                   ind_projs := [] |}];
    ind_universes := Monomorphic_ctx
                       (LevelSetProp.of_list [], ConstraintSet.empty);
    ind_variance := None |});
 (MPfile ["Datatypes"; "Init"; "Coq"], "bool",
 InductiveDecl
   {|
   ind_finite := Finite;
   ind_npars := 0;
   ind_params := [];
   ind_bodies := [{|
                  ind_name := "bool";
                  ind_type := tSort
                                (Universe.from_kernel_repr
                                   (Level.lSet, false) []);
                  ind_kelim := InType;
                  ind_ctors := [("true", tRel 0, 0); ("false", tRel 0, 0)];
                  ind_projs := [] |}];
   ind_universes := Monomorphic_ctx
                      (LevelSetProp.of_list [], ConstraintSet.empty);
   ind_variance := None |});
 (MPfile ["Datatypes"; "Init"; "Coq"], "nat",
 InductiveDecl
   {|
   ind_finite := Finite;
   ind_npars := 0;
   ind_params := [];
   ind_bodies := [{|
                  ind_name := "nat";
                  ind_type := tSort
                                (Universe.from_kernel_repr
                                   (Level.lSet, false) []);
                  ind_kelim := InType;
                  ind_ctors := [("O", tRel 0, 0);
                               ("S", tProd nAnon (tRel 0) (tRel 1), 1)];
                  ind_projs := [] |}];
   ind_universes := Monomorphic_ctx
                      (LevelSetProp.of_list [], ConstraintSet.empty);
   ind_variance := None |})],
tInd {| inductive_mind := (MPfile ["myfilename"], "C"); inductive_ind := 0 |} [])
     : program

where "myfilename" is the name of the source file.

Quite verbose, yes, but it's the AST of the Coq source, after all.

This quoted term that we got is of type program which is defined like

(* metacoq/template-coq/theories/Environment.v *)
Definition program : Type := global_env * term.

So here, the global_env term is (skipping most parts of as it has already been mentioned):

[(MPfile ["myfilename"], "C",
  InductiveDecl
    {|
    ind_finite := Finite;
    ind_npars := 0;
    ind_params := [];
    ind_bodies := [{|
    ...
    ...
                   ind_projs := [] |}];
    ind_universes := Monomorphic_ctx
                       (LevelSetProp.of_list [], ConstraintSet.empty);
    ind_variance := None |});
 (MPfile ["Datatypes"; "Init"; "Coq"], "bool",
 InductiveDecl
   {|
   ind_finite := Finite;
   ind_npars := 0;
   ind_params := [];
   ind_bodies := [{|
                  ind_name := "bool";
                  ind_type := tSort
                                (Universe.from_kernel_repr
                                   (Level.lSet, false) []);
                  ind_kelim := InType;
                  ind_ctors := [("true", tRel 0, 0); ("false", tRel 0, 0)];
                  ind_projs := [] |}];
   ind_universes := Monomorphic_ctx
                      (LevelSetProp.of_list [], ConstraintSet.empty);
   ind_variance := None |});
 (MPfile ["Datatypes"; "Init"; "Coq"], "nat",
 InductiveDecl
   ...
   ...
    ind_variance := None |})].

and the term term is:

= tInd
         {|
         inductive_mind := (MPfile ["myfilename"], "C");
         inductive_ind := 0 |} []
     : term

The constructors seem to be in the global_env term.

Let's get the global_env term from the program value:

Definition aux1 (p : program) : global_env := fst p.
Compute aux1 qC.

Within the global_env term, the constructors appears to be in the ind_bodies field of the inductiveDecl record.

global_env is defined like:

(* metacoq/template-coq/theories/Environment.v *)
Definition global_env : Type := list (kername * global_decl).

We need the first value in this list.

Could use List.hd for that, but since List.hd is defined like

(* theories/Lists/List.v *)
Definition hd (A : Type) (default:A) (l:list A) :=
  match l with
  | [] => default
  | x :: _ => x
  end.

in a way that requires a default value, we need a default (kername * global_decl) value (the 'quest' to find default values is included at the end of this post as an addendum).

Definition default_global_decl : global_decl :=
  ConstantDecl (Build_constant_body
    (tVar ""%string)
    None
    (Monomorphic_ctx
      (LevelSet.Mkt []%list, ConstraintSet.Mkt []%list))).

Definition default_kername : kername :=
  (MPfile []%list, ""%string).

Definition default_kg : (kername * global_decl) :=
  (default_kername, default_global_decl).

Okay, now that we have got a default (kername * global_decl) as default_kg, let's get the first (kername * global_decl).

Definition aux2 (g : global_env) : (kername * global_decl) :=
  List.hd default_kg g.

Compute aux2 (aux1 qC).
(*
= (MPfile ["myfilename"], "C",
       InductiveDecl
         {|
         ind_finite := Finite;
         ind_npars := 0;
         ind_params := [];
         ind_bodies := [{|
                        ind_name := "C";
                        ind_type := tSort
                                      {|
                                      Universe.t_set := {|
                                                 UnivExprSet.this := [UnivExpr.npe
                                                 (NoPropLevel.lSet, false)];
                                                 UnivExprSet.is_ok := UnivExprSet.Raw.singleton_ok
                                                 (UnivExpr.npe
                                                 (NoPropLevel.lSet, false)) |};
                                      Universe.t_ne := eq_refl |};
                        ind_kelim := InType;
                        ind_ctors := [("r", tRel 0, 0);
            ...
            ...
                        ];
                        ind_projs := [] |}];
         ind_universes := Monomorphic_ctx
                            ({|
                             LevelSet.this := [];
                             LevelSet.is_ok := LevelSet.Raw.empty_ok |},
                            {|
                            ConstraintSet.this := [];
                            ConstraintSet.is_ok := ConstraintSet.Raw.empty_ok |});
         ind_variance := None |})
     : kername × global_decl
*)

kername is defined like

(* template-coq/theories/BasicAst.v *)
Definition kername : Set := modpath × ident.

So the (kername * global_decl) is actually (modpath * ident) * global_decl which may also be written like modpath * ident * global_decl as coq tuples associate to the left such that (1, 2, 3) and ((1, 2), 3) are same.

Check (1,2,3).
(* (1, 2, 3) : (nat × nat) × nat *)

Check ((1,2),3).
(* (1, 2, 3) : (nat × nat) × nat *)

In the output of aux2 (aux1 qC),

The constructors are in the global_decl part.

Making a function aux3 to get the global_decl,

Definition aux3 (kg : kername * global_decl) : global_decl := snd kg.
Compute aux3 (aux2 (aux1 qC)).
(*
= InductiveDecl
         {|
         ind_finite := Finite;
         ind_npars := 0;
         ind_params := [];
         ind_bodies := [{|
                        ind_name := "C";
                        ind_type := tSort
                                      {|
                                      Universe.t_set := {|
                                                 UnivExprSet.this := [UnivExpr.npe
                                                 (NoPropLevel.lSet, false)];
                                                 UnivExprSet.is_ok := UnivExprSet.Raw.singleton_ok
                                                 (UnivExpr.npe
                                                 (NoPropLevel.lSet, false)) |};
                                      Universe.t_ne := eq_refl |};
                        ind_kelim := InType;
                        ind_ctors := [("r", tRel 0, 0);
                                     ("g",
                                     tProd nAnon
                                       (tInd
                                          {|
                                          inductive_mind := (
                                                 MPfile
                                                 ["Datatypes"; "Init"; "Coq"],
                                                 "nat");
                                          inductive_ind := 0 |} []) 
                                       (tRel 1), 1);
                                     ("b",
                                     tProd nAnon
                                       (tInd
                                          {|
                                          inductive_mind := (
                                                 MPfile
                                                 ["Datatypes"; "Init"; "Coq"],
                                                 "bool");
                                          inductive_ind := 0 |} [])
                                       (tProd nAnon
                                          (tInd
                                             {|
                                             inductive_mind := (
                                                 MPfile
                                                 ["Datatypes"; "Init"; "Coq"],
                                                 "nat");
                                             inductive_ind := 0 |} [])
                                          (tRel 2)), 2)];
                        ind_projs := [] |}];
         ind_universes := Monomorphic_ctx
                            ({|
                             LevelSet.this := [];
                             LevelSet.is_ok := LevelSet.Raw.empty_ok |},
                            {|
                            ConstraintSet.this := [];
                            ConstraintSet.is_ok := ConstraintSet.Raw.empty_ok |});
         ind_variance := None |}
     : global_decl
*)

The constructors are inside the ind_bodies field of the mutual_inductive_body argument to InductiveDecl. ind_bodies has a list one_inductive_body.

Definition aux4 (g : global_decl) : list one_inductive_body := 
  match g with
  | ConstantDecl _ => []%list
  | InductiveDecl mib => mib.(ind_bodies) (* TODO Map *)
  end.

Compute aux4 (aux3 (aux2 (aux1 qC))).
(*
= [{|
        ind_name := "C";
        ind_type := tSort
                      {|
                      Universe.t_set := {|
                                        UnivExprSet.this := [UnivExpr.npe
                                                 (NoPropLevel.lSet, false)];
                                        UnivExprSet.is_ok := UnivExprSet.Raw.singleton_ok
                                                 (UnivExpr.npe
                                                 (NoPropLevel.lSet, false)) |};
                      Universe.t_ne := eq_refl |};
        ind_kelim := InType;
        ind_ctors := [("r", tRel 0, 0);
                     ("g",
                     tProd nAnon
                       (tInd
                          {|
                          inductive_mind := (MPfile
                                               ["Datatypes"; "Init"; "Coq"],
                                            "nat");
                          inductive_ind := 0 |} []) 
                       (tRel 1), 1);
                     ("b",
                     tProd nAnon
                       (tInd
                          {|
                          inductive_mind := (MPfile
                                               ["Datatypes"; "Init"; "Coq"],
                                            "bool");
                          inductive_ind := 0 |} [])
                       (tProd nAnon
                          (tInd
                             {|
                             inductive_mind := (MPfile
                                                 ["Datatypes"; "Init"; "Coq"],
                                               "nat");
                             inductive_ind := 0 |} []) 
                          (tRel 2)), 2)];
        ind_projs := [] |}]
     : list one_inductive_body
*)

Now we are quite close to constructors, which are inside the ind_ctors field of the one_inductive_body values.

Definition aux5 (oibs : list one_inductive_body)
  : list (list (ident * term * nat)) :=
  map (fun o:one_inductive_body => o.(ind_ctors)) oibs.

Compute aux5 (aux4 (aux3 (aux2 (aux1 qC)))). (* CONSTRUCTORS! *)
(*
= [[("r", tRel 0, 0);
        ("g",
        tProd nAnon
          (tInd
             {|
             inductive_mind := (MPfile ["Datatypes"; "Init"; "Coq"], "nat");
             inductive_ind := 0 |} []) (tRel 1), 1);
        ("b",
        tProd nAnon
          (tInd
             {|
             inductive_mind := (MPfile ["Datatypes"; "Init"; "Coq"], "bool");
             inductive_ind := 0 |} [])
          (tProd nAnon
             (tInd
                {|
                inductive_mind := (MPfile ["Datatypes"; "Init"; "Coq"],
                                  "nat");
                inductive_ind := 0 |} []) (tRel 2)), 2)]]
     : list (list ((ident × term) × nat))
*)

Piecing all the 'aux' functions together:

Definition aux (p : program) : list (list (ident * term * nat)) :=
  let genv := fst p in
  let kgd := List.hd default_kg genv in
  let gd := snd kgd in
  let oibs :=
    match gd with
    | ConstantDecl _ => []%list
    | InductiveDecl mib => mib.(ind_bodies)
    end in
  map (fun o:one_inductive_body => o.(ind_ctors)) oibs.

Compute aux qC.
(*
= [[("r", tRel 0, 0);
        ("g",
        tProd nAnon
          (tInd
             {|
             inductive_mind := (MPfile ["Datatypes"; "Init"; "Coq"], "nat");
             inductive_ind := 0 |} []) (tRel 1), 1);
        ("b",
        tProd nAnon
          (tInd
             {|
             inductive_mind := (MPfile ["Datatypes"; "Init"; "Coq"], "bool");
             inductive_ind := 0 |} [])
          (tProd nAnon
             (tInd
                {|
                inductive_mind := (MPfile ["Datatypes"; "Init"; "Coq"],
                                  "nat");
                inductive_ind := 0 |} []) (tRel 2)), 2)]]
     : list (list ((ident × term) × nat))
*)

Which is what we were after.

(Just for fun's sake, let me try this notation here.

Notation "f $ x" := (f x)
  (at level 60, right associativity, only parsing).

Definition aux' (p : program) : list (list (ident * term * nat)) :=
  let gd := snd $ List.hd default_kg $ (fst p) in
  map (fun o:one_inductive_body => o.(ind_ctors)) 
    match gd with
    | ConstantDecl _ => []%list
    | InductiveDecl mib => mib.(ind_bodies)
    end.

Compute aux' qC.

But I had been warned that this can cause scope inference to break.

Okay, side-mission over.)

References

Addendum: Dummy/default values

For use with List.hd which requires a default value.

Being unfamiliar with the Template-Coq types when I used it for the first time, I found it helpful to write them down.

Still not sure if this was necessary, but as of writing this post, I don't know of any other way.

ident

ident is same as string and represents an identifier.

(* template-coq/theories/BasicAst.v *)
Definition ident : Set := string.

modpath

Definition:

(* template-coq/theories/BasicAst.v *)
Inductive modpath : Set :=
| MPfile  (dp : dirpath)
| MPbound (dp : dirpath) (id : ident) (i : nat)
| MPdot   (mp : modpath) (id : ident).

Let's go with the simplest constructor, MPfile.

Using a dummy dirpath value,

Check MPfile []%list : modpath.
(*
MPfile [] : modpath
     : modpath
*)

kername

Definition:

(* template-coq/theories/BasicAst.v *)
Definition kername : Set := modpath × ident.

Using dummy modpath and ident values (where ident is same as string),

Check (MPfile []%list, ""%string) : kername.
(*
(MPfile [], "") : kername
     : kername
*)

dirpath

Definition:

(* template-coq/theories/BasicAst.v *)
Definition dirpath : Set := list ident.

which means it is basically list string.

A list! Easy. An empty list would do.

Check []%list : dirpath.
(*
[] : dirpath
     : dirpath
*)

global_decl

Definition:

Inductive global_decl : Type :=
  (* Represents declaration of constants, I guess *)
  | ConstantDecl : constant_body -> global_decl

  (* Represents declaration of inductive types, probably *)
  | InductiveDecl : mutual_inductive_body -> global_decl.

Simplest constructor is ConstantDecl, let's go with that.

Using a dummy constant_body value,

Check ConstantDecl (Build_constant_body (tVar ""%string) None
  (Monomorphic_ctx ((LevelSet.Mkt []%list), (ConstraintSet.Mkt []%list)) : universes_decl)) : global_decl.
(*
ConstantDecl
  {|
  cst_type := tVar "";
  cst_body := None;
  cst_universes := Monomorphic_ctx
                     ({|
                      LevelSet.this := [];
                      LevelSet.is_ok := LevelSet.Raw.empty_ok |},
                     {|
                     ConstraintSet.this := [];
                     ConstraintSet.is_ok := ConstraintSet.Raw.empty_ok |})
                   :
                   universes_decl |}
:
global_decl
     : global_decl
*)

constant_body

Definition:

Record constant_body : Type := Build_constant_body {
  cst_type : term;
  cst_body : option term;
  cst_universes : universes_decl
}.

Simplest option value is, of course, None.

Using dummy universe_decl and terms values with this,

Check Build_constant_body (tVar ""%string) None
  (Monomorphic_ctx ((LevelSet.Mkt []%list), (ConstraintSet.Mkt []%list)) : universes_decl) : constant_body.
(*
{|
cst_type := tVar "";
cst_body := None;
cst_universes := Monomorphic_ctx
                   ({|
                    LevelSet.this := [];
                    LevelSet.is_ok := LevelSet.Raw.empty_ok |},
                   {|
                   ConstraintSet.this := [];
                   ConstraintSet.is_ok := ConstraintSet.Raw.empty_ok |})
                 :
                 universes_decl |}
:
constant_body
     : constant_body
*)

term

Definition:

Inductive term : Type :=
    tRel : nat -> term
  | tVar : ident -> term
  | tEvar : nat -> list term -> term
  | tSort : Universe.t -> term
  | tCast : term -> cast_kind -> term -> term
  | tProd : name -> term -> term -> term
  | tLambda : name -> term -> term -> term
  | tLetIn : name -> term -> term -> term -> term
  | tApp : term -> list term -> term
  | tConst : kername -> Instance.t -> term
  | tInd : inductive -> Instance.t -> term
  | tConstruct : inductive -> nat -> Instance.t -> term
  | tCase : inductive × nat -> term -> term -> list (nat × term) -> term
  | tProj : projection -> term -> term
  | tFix : mfixpoint term -> nat -> term
  | tCoFix : mfixpoint term -> nat -> term

Arguments tRel _%nat_scope
Arguments tEvar _%nat_scope _%list_scope
Arguments tApp _ _%list_scope
Arguments tConstruct _ _%nat_scope
Arguments tCase _ _ _ _%list_scope
Arguments tFix _ _%nat_scope
Arguments tCoFix _ _%nat_scope

tVar seems simple enough.

Check (tVar ""%string) : term.
(*
tVar "" : term
     : term
*)

universes_decl

Definition:

Inductive universes_decl : Type :=
    Monomorphic_ctx : ContextSet.t -> universes_decl
  | Polymorphic_ctx : AUContext.t -> universes_decl

Let's go with Monomorphic_ctx (because monomorphic got to be simpler than polymorphic, right?). Using a dummy ContextSet.t value,

Check Monomorphic_ctx ((LevelSet.Mkt []%list), (ConstraintSet.Mkt []%list)) : universes_decl.
(*
Monomorphic_ctx
  ({| LevelSet.this := []; LevelSet.is_ok := LevelSet.Raw.empty_ok |},
  {|
  ConstraintSet.this := [];
  ConstraintSet.is_ok := ConstraintSet.Raw.empty_ok |})
:
universes_decl
     : universes_decl
*)

ContextSet.t

Definition:


ContextSet.t = LevelSet.t × ConstraintSet.t
     : Type

Using dummy LevelSet.t and ConstraintSet.t values,

Check ((LevelSet.Mkt []%list), (ConstraintSet.Mkt []%list)) : ContextSet.t.
(*
({| LevelSet.this := []; LevelSet.is_ok := LevelSet.Raw.empty_ok |},
{|
ConstraintSet.this := [];
ConstraintSet.is_ok := ConstraintSet.Raw.empty_ok |})
:
ContextSet.t
     : ContextSet.t
*)

LevelSet.t

Definition:

LevelSet.t = LevelSet.t_
     : Type

Using a dummy LevelSet.t_ value,

Check LevelSet.Mkt []%list : LevelSet.t.
(*
{| LevelSet.this := []; LevelSet.is_ok := LevelSet.Raw.empty_ok |}
:
LevelSet.t
     : LevelSet.t
*)

LevelSet.t_

Definition:

Record t_ : Type := Mkt {
  this : LevelSet.Raw.t;
  is_ok : LevelSet.Raw.Ok this
}.

Arguments LevelSet.Mkt _ {is_ok}.

is_ok is implicit. Using a random LevelSet.Raw.t,

Check LevelSet.Mkt []%list : LevelSet.t_.
(*
{| LevelSet.this := []; LevelSet.is_ok := LevelSet.Raw.empty_ok |}
:
LevelSet.t_
     : LevelSet.t_
*)

LevelSet.Raw.t

LevelSet.Raw.t = list LevelSet.Raw.elt
     : Type

A list. So we can stop here with a simple []%list.

Check []%list : LevelSet.Raw.t.
(*
[] : LevelSet.Raw.t
     : LevelSet.Raw.t
*)

ConstraintSet.t

Definition:

ConstraintSet.t = ConstraintSet.t_
     : Type

Using dummy ConstraintSet.t_ value,

Check ConstraintSet.Mkt []%list : ConstraintSet.t.
(*
{|
ConstraintSet.this := [];
ConstraintSet.is_ok := ConstraintSet.Raw.empty_ok |}
:
ConstraintSet.t
     : ConstraintSet.t
*)

ConstraintSet.t_

Record t_ : Type := Mkt {
  this : ConstraintSet.Raw.t;
  is_ok : ConstraintSet.Raw.Ok this
}.

Arguments ConstraintSet.Mkt _ {is_ok}

is_ok is implicit. Using a dummy ConstraintSet.Raw.t,

Check ConstraintSet.Mkt []%list : ConstraintSet.t_.
(*
{|
ConstraintSet.this := [];
ConstraintSet.is_ok := ConstraintSet.Raw.empty_ok |}
:
ConstraintSet.t_
     : ConstraintSet.t_
*)

ConstraintSet.Raw.t

ConstraintSet.Raw.t = list ConstraintSet.Raw.elt
     : Type

A list!

Check []%list : ConstraintSet.Raw.t.
(*
[] : ConstraintSet.Raw.t
     : ConstraintSet.Raw.t
*)

recursivity_kind

Definition:

Inductive recursivity_kind : Set :=
    Finite : recursivity_kind
  | CoFinite : recursivity_kind
  | BiFinite : recursivity_kind

Going with Finite.

Check Finite : recursivity_kind.
(*
Finite : recursivity_kind
     : recursivity_kind
*)

mutual_inductive_body

Record mutual_inductive_body : Type := Build_mutual_inductive_body
  { ind_finite : recursivity_kind;
    ind_npars : nat;
    ind_params : context;
    ind_bodies : list one_inductive_body;
    ind_universes : universes_decl;
    ind_variance : option (list Variance.t) }

Arguments Build_mutual_inductive_body _ _%nat_scope _ _%list_scope

Using dummy values,

Compute Build_mutual_inductive_body Finite 0 []%list []%list
(Monomorphic_ctx (LevelSet.Mkt []%list, ConstraintSet.Mkt []%list)) None.
(*
= {|
       ind_finite := Finite;
       ind_npars := 0;
       ind_params := [];
       ind_bodies := [];
       ind_universes := Monomorphic_ctx
                          ({|
                           LevelSet.this := [];
                           LevelSet.is_ok := LevelSet.Raw.empty_ok |},
                          {|
                          ConstraintSet.this := [];
                          ConstraintSet.is_ok := ConstraintSet.Raw.empty_ok |});
       ind_variance := None |}
     : mutual_inductive_body
*)

context

Definition:

context = list context_decl
     : Type

List. Empty list would do.

Check []%list : context.
(*
[] : context
     : context
*)

sort_family

Definition:

Inductive sort_family : Set :=
    InProp : sort_family | InSet : sort_family | InType : sort_family

Any of the three would do. Let's go with InProp.

Check InProp : sort_family.
(*
InProp : sort_family
     : sort_family
*)

one_inductive_body

Definition:

Record one_inductive_body : Type := Build_one_inductive_body
  { ind_name : ident;
    ind_type : term;
    ind_kelim : sort_family;
    ind_ctors : list ((ident × term) × nat);
    ind_projs : list (ident × term) }

Arguments Build_one_inductive_body _ _ _ (_ _)%list_scope

Using dummy ident, term and sort_family values,

Check Build_one_inductive_body "None"%string (tVar "None"%string) InProp []%list []%list.
(*
{|
ind_name := "None";
ind_type := tVar "None";
ind_kelim := InProp;
ind_ctors := [];
ind_projs := [] |}
     : one_inductive_body
*)

(This blog post was originally completed by 07 January 2022.)