feat(frontends/lean): make the first argument of if-expression implicit, add support for marking implicit arguments on builtin symbols (aka semantic attachments)
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
parent
2d88922543
commit
d0009d0242
12 changed files with 55 additions and 28 deletions
|
@ -241,7 +241,7 @@ struct frontend::imp {
|
||||||
if (has_children())
|
if (has_children())
|
||||||
throw exception(sstream() << "failed to mark implicit arguments, frontend object is read-only");
|
throw exception(sstream() << "failed to mark implicit arguments, frontend object is read-only");
|
||||||
object const & obj = m_env.get_object(n);
|
object const & obj = m_env.get_object(n);
|
||||||
if (obj.kind() != object_kind::Definition && obj.kind() != object_kind::Postulate)
|
if (obj.kind() != object_kind::Definition && obj.kind() != object_kind::Postulate && obj.kind() != object_kind::Builtin)
|
||||||
throw exception(sstream() << "failed to mark implicit arguments, the object '" << n << "' is not a definition or postulate");
|
throw exception(sstream() << "failed to mark implicit arguments, the object '" << n << "' is not a definition or postulate");
|
||||||
if (has_implicit_arguments(n))
|
if (has_implicit_arguments(n))
|
||||||
throw exception(sstream() << "the object '" << n << "' already has implicit argument information associated with it");
|
throw exception(sstream() << "the object '" << n << "' already has implicit argument information associated with it");
|
||||||
|
@ -425,6 +425,14 @@ operator_info frontend::find_led(name const & n) const { return m_ptr->find_led(
|
||||||
|
|
||||||
void frontend::mark_implicit_arguments(name const & n, unsigned sz, bool const * implicit) { m_ptr->mark_implicit_arguments(n, sz, implicit); }
|
void frontend::mark_implicit_arguments(name const & n, unsigned sz, bool const * implicit) { m_ptr->mark_implicit_arguments(n, sz, implicit); }
|
||||||
void frontend::mark_implicit_arguments(name const & n, std::initializer_list<bool> const & l) { mark_implicit_arguments(n, l.size(), l.begin()); }
|
void frontend::mark_implicit_arguments(name const & n, std::initializer_list<bool> const & l) { mark_implicit_arguments(n, l.size(), l.begin()); }
|
||||||
|
void frontend::mark_implicit_arguments(expr const & n, std::initializer_list<bool> const & l) {
|
||||||
|
if (is_constant(n)) {
|
||||||
|
mark_implicit_arguments(const_name(n), l);
|
||||||
|
} else {
|
||||||
|
lean_assert(is_value(n));
|
||||||
|
mark_implicit_arguments(to_value(n).get_name(), l);
|
||||||
|
}
|
||||||
|
}
|
||||||
bool frontend::has_implicit_arguments(name const & n) const { return m_ptr->has_implicit_arguments(n); }
|
bool frontend::has_implicit_arguments(name const & n) const { return m_ptr->has_implicit_arguments(n); }
|
||||||
std::vector<bool> const & frontend::get_implicit_arguments(name const & n) const { return m_ptr->get_implicit_arguments(n); }
|
std::vector<bool> const & frontend::get_implicit_arguments(name const & n) const { return m_ptr->get_implicit_arguments(n); }
|
||||||
name const & frontend::get_explicit_version(name const & n) const { return m_ptr->get_explicit_version(n); }
|
name const & frontend::get_explicit_version(name const & n) const { return m_ptr->get_explicit_version(n); }
|
||||||
|
|
|
@ -135,7 +135,7 @@ public:
|
||||||
*/
|
*/
|
||||||
void mark_implicit_arguments(name const & n, unsigned sz, bool const * mask);
|
void mark_implicit_arguments(name const & n, unsigned sz, bool const * mask);
|
||||||
void mark_implicit_arguments(name const & n, std::initializer_list<bool> const & l);
|
void mark_implicit_arguments(name const & n, std::initializer_list<bool> const & l);
|
||||||
void mark_implicit_arguments(expr const & n, std::initializer_list<bool> const & l) { mark_implicit_arguments(const_name(n), l); }
|
void mark_implicit_arguments(expr const & n, std::initializer_list<bool> const & l);
|
||||||
/** \brief Return true iff \c n has implicit arguments */
|
/** \brief Return true iff \c n has implicit arguments */
|
||||||
bool has_implicit_arguments(name const & n) const;
|
bool has_implicit_arguments(name const & n) const;
|
||||||
/** \brief Return the position of the arguments that are implicit. */
|
/** \brief Return the position of the arguments that are implicit. */
|
||||||
|
|
|
@ -16,6 +16,7 @@ namespace lean {
|
||||||
*/
|
*/
|
||||||
void init_builtin_notation(frontend & f) {
|
void init_builtin_notation(frontend & f) {
|
||||||
f.mark_implicit_arguments(mk_homo_eq_fn(), {true, false, false});
|
f.mark_implicit_arguments(mk_homo_eq_fn(), {true, false, false});
|
||||||
|
f.mark_implicit_arguments(mk_if_fn(), {true, false, false, false});
|
||||||
f.add_infix("==", 50, mk_homo_eq_fn());
|
f.add_infix("==", 50, mk_homo_eq_fn());
|
||||||
f.add_infix("≃", 50, mk_homo_eq_fn());
|
f.add_infix("≃", 50, mk_homo_eq_fn());
|
||||||
|
|
||||||
|
|
|
@ -545,12 +545,13 @@ class parser::imp {
|
||||||
object const & obj = m_frontend.find_object(id);
|
object const & obj = m_frontend.find_object(id);
|
||||||
if (obj) {
|
if (obj) {
|
||||||
object_kind k = obj.kind();
|
object_kind k = obj.kind();
|
||||||
if (k == object_kind::Definition || k == object_kind::Postulate) {
|
if (k == object_kind::Definition || k == object_kind::Postulate || k == object_kind::Builtin) {
|
||||||
if (m_frontend.has_implicit_arguments(obj.get_name())) {
|
if (m_frontend.has_implicit_arguments(obj.get_name())) {
|
||||||
std::vector<bool> const & imp_args = m_frontend.get_implicit_arguments(obj.get_name());
|
std::vector<bool> const & imp_args = m_frontend.get_implicit_arguments(obj.get_name());
|
||||||
buffer<expr> args;
|
buffer<expr> args;
|
||||||
pos_info p = pos();
|
pos_info p = pos();
|
||||||
args.push_back(save(mk_constant(obj.get_name()), p));
|
expr f = (k == object_kind::Builtin) ? obj.get_value() : mk_constant(obj.get_name());
|
||||||
|
args.push_back(save(f, p));
|
||||||
// We parse all the arguments to make sure we
|
// We parse all the arguments to make sure we
|
||||||
// get all explicit arguments.
|
// get all explicit arguments.
|
||||||
for (unsigned i = 0; i < imp_args.size(); i++) {
|
for (unsigned i = 0; i < imp_args.size(); i++) {
|
||||||
|
@ -561,11 +562,11 @@ class parser::imp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return mk_app(args);
|
return mk_app(args);
|
||||||
|
} else if (k == object_kind::Builtin) {
|
||||||
|
return obj.get_value();
|
||||||
} else {
|
} else {
|
||||||
return mk_constant(obj.get_name());
|
return mk_constant(obj.get_name());
|
||||||
}
|
}
|
||||||
} else if (k == object_kind::Builtin) {
|
|
||||||
return obj.get_value();
|
|
||||||
} else {
|
} else {
|
||||||
throw parser_error(sstream() << "invalid object reference, object '" << id << "' is not an expression.", p);
|
throw parser_error(sstream() << "invalid object reference, object '" << id << "' is not an expression.", p);
|
||||||
}
|
}
|
||||||
|
|
|
@ -244,7 +244,12 @@ class pp_fn {
|
||||||
}
|
}
|
||||||
|
|
||||||
result pp_value(expr const & e) {
|
result pp_value(expr const & e) {
|
||||||
return mk_result(to_value(e).pp(m_unicode), 1);
|
value const & v = to_value(e);
|
||||||
|
if (has_implicit_arguments(v.get_name())) {
|
||||||
|
return mk_result(format(m_frontend.get_explicit_version(v.get_name())), 1);
|
||||||
|
} else {
|
||||||
|
return mk_result(v.pp(m_unicode), 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result pp_type(expr const & e) {
|
result pp_type(expr const & e) {
|
||||||
|
@ -506,18 +511,25 @@ class pp_fn {
|
||||||
std::vector<bool> const * m_implicit_args;
|
std::vector<bool> const * m_implicit_args;
|
||||||
bool m_notation_enabled;
|
bool m_notation_enabled;
|
||||||
|
|
||||||
|
static bool has_implicit_arguments(pp_fn const & owner, expr const & f) {
|
||||||
|
return
|
||||||
|
(is_constant(f) && owner.has_implicit_arguments(const_name(f))) ||
|
||||||
|
(is_value(f) && owner.has_implicit_arguments(to_value(f).get_name()));
|
||||||
|
}
|
||||||
|
|
||||||
application(expr const & e, pp_fn const & owner, bool show_implicit):m_app(e) {
|
application(expr const & e, pp_fn const & owner, bool show_implicit):m_app(e) {
|
||||||
frontend const & fe = owner.m_frontend;
|
frontend const & fe = owner.m_frontend;
|
||||||
expr const & f = arg(e, 0);
|
expr const & f = arg(e, 0);
|
||||||
if (is_constant(f) && owner.has_implicit_arguments(const_name(f))) {
|
if (has_implicit_arguments(owner, f)) {
|
||||||
m_implicit_args = &(fe.get_implicit_arguments(const_name(f)));
|
name const & n = is_constant(f) ? const_name(f) : to_value(f).get_name();
|
||||||
|
m_implicit_args = &(fe.get_implicit_arguments(n));
|
||||||
if (show_implicit || num_args(e) - 1 < m_implicit_args->size()) {
|
if (show_implicit || num_args(e) - 1 < m_implicit_args->size()) {
|
||||||
// we are showing implicit arguments, thus we do
|
// we are showing implicit arguments, thus we do
|
||||||
// not need the bit-mask for implicit arguments
|
// not need the bit-mask for implicit arguments
|
||||||
m_implicit_args = nullptr;
|
m_implicit_args = nullptr;
|
||||||
// we use the explicit name of f, to make it clear
|
// we use the explicit name of f, to make it clear
|
||||||
// that we are exposing implicit arguments
|
// that we are exposing implicit arguments
|
||||||
m_f = mk_constant(fe.get_explicit_version(const_name(f)));
|
m_f = mk_constant(fe.get_explicit_version(n));
|
||||||
m_notation_enabled = false;
|
m_notation_enabled = false;
|
||||||
} else {
|
} else {
|
||||||
m_f = f;
|
m_f = f;
|
||||||
|
@ -683,7 +695,13 @@ class pp_fn {
|
||||||
} else {
|
} else {
|
||||||
// standard function application
|
// standard function application
|
||||||
expr const & f = app.get_function();
|
expr const & f = app.get_function();
|
||||||
result p = is_constant(f) ? mk_result(format(const_name(f)), 1) : pp_child(f, depth);
|
result p;
|
||||||
|
if (is_constant(f))
|
||||||
|
p = mk_result(format(const_name(f)), 1);
|
||||||
|
else if (is_value(f))
|
||||||
|
p = mk_result(to_value(f).pp(m_unicode), 1);
|
||||||
|
else
|
||||||
|
p = pp_child(f, depth);
|
||||||
bool simple = is_constant(f) && const_name(f).size() <= m_indent + 4;
|
bool simple = is_constant(f) && const_name(f).size() <= m_indent + 4;
|
||||||
unsigned indent = simple ? const_name(f).size()+1 : m_indent;
|
unsigned indent = simple ? const_name(f).size()+1 : m_indent;
|
||||||
format r_format = p.first;
|
format r_format = p.first;
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
2
|
2
|
||||||
⊤
|
⊤
|
||||||
Assumed: y
|
Assumed: y
|
||||||
if ℤ (0 ≤ -3 + y) (-3 + y) (-1 * (-3 + y))
|
if (0 ≤ -3 + y) (-3 + y) (-1 * (-3 + y))
|
||||||
| x + y | > x
|
| x + y | > x
|
||||||
Set: lean::pp::notation
|
Set: lean::pp::notation
|
||||||
Int::gt (Int::abs (Int::add x y)) x
|
Int::gt (Int::abs (Int::add x y)) x
|
||||||
|
|
|
@ -553,7 +553,7 @@ Failed to solve
|
||||||
⊢ ?M3::1 ≈ Type U
|
⊢ ?M3::1 ≈ Type U
|
||||||
Assumption 29
|
Assumption 29
|
||||||
Failed to solve
|
Failed to solve
|
||||||
a : Bool, b : Bool, H : ?M3::2, H_na : ?M3::7 ⊢ a ≺ if Bool (if Bool a b ⊤) a ⊤
|
a : Bool, b : Bool, H : ?M3::2, H_na : ?M3::7 ⊢ a ≺ if (if a b ⊤) a ⊤
|
||||||
Substitution
|
Substitution
|
||||||
a : Bool, b : Bool, H : ?M3::2, H_na : ?M3::7 ⊢ a ≺ ?M3::5[lift:0:1]
|
a : Bool, b : Bool, H : ?M3::2, H_na : ?M3::7 ⊢ a ≺ ?M3::5[lift:0:1]
|
||||||
Substitution
|
Substitution
|
||||||
|
@ -572,29 +572,29 @@ a : Bool, b : Bool, H : ?M3::2, H_na : ?M3::7 ⊢ a ≺ if Bool (if Bool a b ⊤
|
||||||
Assignment
|
Assignment
|
||||||
a : Bool, b : Bool, H : ?M3::2, H_na : ?M3::7 ⊢ a ≈ ?M3::8
|
a : Bool, b : Bool, H : ?M3::2, H_na : ?M3::7 ⊢ a ≈ ?M3::8
|
||||||
Destruct/Decompose
|
Destruct/Decompose
|
||||||
a : Bool, b : Bool, H : ?M3::2, H_na : ?M3::7 ⊢ if Bool a b ⊤ ≈ if Bool ?M3::8 ?M3::9 ⊤
|
a : Bool, b : Bool, H : ?M3::2, H_na : ?M3::7 ⊢ if a b ⊤ ≈ if ?M3::8 ?M3::9 ⊤
|
||||||
Normalize
|
Normalize
|
||||||
a : Bool, b : Bool, H : ?M3::2, H_na : ?M3::7 ⊢ if Bool a b ⊤ ≈ ?M3::8 ⇒ ?M3::9
|
a : Bool, b : Bool, H : ?M3::2, H_na : ?M3::7 ⊢ if a b ⊤ ≈ ?M3::8 ⇒ ?M3::9
|
||||||
Substitution
|
Substitution
|
||||||
a : Bool, b : Bool, H : ?M3::2, H_na : ?M3::7 ⊢ if Bool a b ⊤ ≈ ?M3::10
|
a : Bool, b : Bool, H : ?M3::2, H_na : ?M3::7 ⊢ if a b ⊤ ≈ ?M3::10
|
||||||
Destruct/Decompose
|
Destruct/Decompose
|
||||||
a : Bool,
|
a : Bool,
|
||||||
b : Bool,
|
b : Bool,
|
||||||
H : ?M3::2,
|
H : ?M3::2,
|
||||||
H_na : ?M3::7 ⊢
|
H_na : ?M3::7 ⊢
|
||||||
if Bool (if Bool a b ⊤) a ⊤ ≺ if Bool ?M3::10 ?M3::11 ⊤
|
if (if a b ⊤) a ⊤ ≺ if ?M3::10 ?M3::11 ⊤
|
||||||
Normalize
|
Normalize
|
||||||
a : Bool,
|
a : Bool,
|
||||||
b : Bool,
|
b : Bool,
|
||||||
H : ?M3::2,
|
H : ?M3::2,
|
||||||
H_na : ?M3::7 ⊢
|
H_na : ?M3::7 ⊢
|
||||||
(a ⇒ b) ⇒ a ≺ if Bool ?M3::10 ?M3::11 ⊤
|
(a ⇒ b) ⇒ a ≺ if ?M3::10 ?M3::11 ⊤
|
||||||
Substitution
|
Substitution
|
||||||
a : Bool,
|
a : Bool,
|
||||||
b : Bool,
|
b : Bool,
|
||||||
H : ?M3::2,
|
H : ?M3::2,
|
||||||
H_na : ?M3::7 ⊢
|
H_na : ?M3::7 ⊢
|
||||||
?M3::2[lift:0:2] ≺ if Bool ?M3::10 ?M3::11 ⊤
|
?M3::2[lift:0:2] ≺ if ?M3::10 ?M3::11 ⊤
|
||||||
Normalize
|
Normalize
|
||||||
a : Bool,
|
a : Bool,
|
||||||
b : Bool,
|
b : Bool,
|
||||||
|
@ -650,7 +650,7 @@ a : Bool, b : Bool, H : ?M3::2, H_na : ?M3::7 ⊢ a ≺ if Bool (if Bool a b ⊤
|
||||||
?M3::9
|
?M3::9
|
||||||
MT H H_na
|
MT H H_na
|
||||||
Assignment
|
Assignment
|
||||||
a : Bool, b : Bool, H : ?M3::2 ⊢ if Bool (if Bool a b ⊤) a ⊤ ≺ ?M3::5
|
a : Bool, b : Bool, H : ?M3::2 ⊢ if (if a b ⊤) a ⊤ ≺ ?M3::5
|
||||||
Normalize
|
Normalize
|
||||||
a : Bool, b : Bool, H : ?M3::2 ⊢ (a ⇒ b) ⇒ a ≺ ?M3::5
|
a : Bool, b : Bool, H : ?M3::2 ⊢ (a ⇒ b) ⇒ a ≺ ?M3::5
|
||||||
Normalize
|
Normalize
|
||||||
|
|
|
@ -7,4 +7,4 @@
|
||||||
Assumed: H2
|
Assumed: H2
|
||||||
Proved: T1
|
Proved: T1
|
||||||
Axiom H2 : (g a) > 0
|
Axiom H2 : (g a) > 0
|
||||||
Theorem T1 : (g b) > 0 := Subst (λ x : ℤ, if Bool ((g x) ≤ 0) ⊥ ⊤) H2 H1
|
Theorem T1 : (g b) > 0 := Subst (λ x : ℤ, if ((g x) ≤ 0) ⊥ ⊤) H2 H1
|
||||||
|
|
|
@ -9,7 +9,7 @@ Infix 50 < : lt
|
||||||
Axiom two_lt_three : two < three
|
Axiom two_lt_three : two < three
|
||||||
Definition vector (A : Type) (n : N) : Type := Pi (i : N) (H : i < n), A
|
Definition vector (A : Type) (n : N) : Type := Pi (i : N) (H : i < n), A
|
||||||
Definition const {A : Type} (n : N) (d : A) : vector A n := fun (i : N) (H : i < n), d
|
Definition const {A : Type} (n : N) (d : A) : vector A n := fun (i : N) (H : i < n), d
|
||||||
Definition update {A : Type} {n : N} (v : vector A n) (i : N) (d : A) : vector A n := fun (j : N) (H : j < n), if A (j = i) d (v j H)
|
Definition update {A : Type} {n : N} (v : vector A n) (i : N) (d : A) : vector A n := fun (j : N) (H : j < n), if (j = i) d (v j H)
|
||||||
Definition select {A : Type} {n : N} (v : vector A n) (i : N) (H : i < n) : A := v i H
|
Definition select {A : Type} {n : N} (v : vector A n) (i : N) (H : i < n) : A := v i H
|
||||||
Definition map {A B C : Type} {n : N} (f : A -> B -> C) (v1 : vector A n) (v2 : vector B n) : vector C n := fun (i : N) (H : i < n), f (v1 i H) (v2 i H)
|
Definition map {A B C : Type} {n : N} (f : A -> B -> C) (v1 : vector A n) (v2 : vector B n) : vector C n := fun (i : N) (H : i < n), f (v1 i H) (v2 i H)
|
||||||
Show Environment 10
|
Show Environment 10
|
||||||
|
|
|
@ -17,7 +17,7 @@ Definition vector (A : Type) (n : N) : Type := Π (i : N), (i < n) → A
|
||||||
Definition const {A : Type} (n : N) (d : A) : vector A n := λ (i : N) (H : i < n), d
|
Definition const {A : Type} (n : N) (d : A) : vector A n := λ (i : N) (H : i < n), d
|
||||||
Definition const::explicit (A : Type) (n : N) (d : A) : vector A n := const n d
|
Definition const::explicit (A : Type) (n : N) (d : A) : vector A n := const n d
|
||||||
Definition update {A : Type} {n : N} (v : vector A n) (i : N) (d : A) : vector A n :=
|
Definition update {A : Type} {n : N} (v : vector A n) (i : N) (d : A) : vector A n :=
|
||||||
λ (j : N) (H : j < n), if A (j = i) d (v j H)
|
λ (j : N) (H : j < n), if (j = i) d (v j H)
|
||||||
Definition update::explicit (A : Type) (n : N) (v : vector A n) (i : N) (d : A) : vector A n := update v i d
|
Definition update::explicit (A : Type) (n : N) (v : vector A n) (i : N) (d : A) : vector A n := update v i d
|
||||||
Definition select {A : Type} {n : N} (v : vector A n) (i : N) (H : i < n) : A := v i H
|
Definition select {A : Type} {n : N} (v : vector A n) (i : N) (H : i < n) : A := v i H
|
||||||
Definition select::explicit (A : Type) (n : N) (v : vector A n) (i : N) (H : i < n) : A := select v i H
|
Definition select::explicit (A : Type) (n : N) (v : vector A n) (i : N) (H : i < n) : A := select v i H
|
||||||
|
@ -26,7 +26,7 @@ Definition map {A B C : Type} {n : N} (f : A → B → C) (v1 : vector A n) (v2
|
||||||
Definition map::explicit (A B C : Type) (n : N) (f : A → B → C) (v1 : vector A n) (v2 : vector B n) : vector C n :=
|
Definition map::explicit (A B C : Type) (n : N) (f : A → B → C) (v1 : vector A n) (v2 : vector B n) : vector C n :=
|
||||||
map f v1 v2
|
map f v1 v2
|
||||||
select (update (const three ⊥) two ⊤) two two_lt_three : Bool
|
select (update (const three ⊥) two ⊤) two two_lt_three : Bool
|
||||||
if Bool (two = two) ⊤ ⊥
|
if (two = two) ⊤ ⊥
|
||||||
update (const three ⊥) two ⊤ : vector Bool three
|
update (const three ⊥) two ⊤ : vector Bool three
|
||||||
|
|
||||||
--------
|
--------
|
||||||
|
@ -46,4 +46,4 @@ map normal form -->
|
||||||
f (v1 i H) (v2 i H)
|
f (v1 i H) (v2 i H)
|
||||||
|
|
||||||
update normal form -->
|
update normal form -->
|
||||||
λ (A : Type) (n : N) (v : Π (i : N), (i < n) → A) (i : N) (d : A) (j : N) (H : j < n), if A (j = i) d (v j H)
|
λ (A : Type) (n : N) (v : Π (i : N), (i < n) → A) (i : N) (d : A) (j : N) (H : j < n), if (j = i) d (v j H)
|
||||||
|
|
|
@ -15,7 +15,7 @@ a ∨ b
|
||||||
a ∨ a ∨ b
|
a ∨ a ∨ b
|
||||||
a ⇒ b ⇒ a
|
a ⇒ b ⇒ a
|
||||||
a ⇒ b : Bool
|
a ⇒ b : Bool
|
||||||
if Bool a a ⊤
|
if a a ⊤
|
||||||
a
|
a
|
||||||
Assumed: H1
|
Assumed: H1
|
||||||
Assumed: H2
|
Assumed: H2
|
||||||
|
|
|
@ -5,6 +5,5 @@
|
||||||
∀ a b : Type, ∃ c : Type, (g a b) = (f c)
|
∀ a b : Type, ∃ c : Type, (g a b) = (f c)
|
||||||
∀ a b : Type, ∃ c : Type, (g a b) = (f c) : Bool
|
∀ a b : Type, ∃ c : Type, (g a b) = (f c) : Bool
|
||||||
(λ a : Type,
|
(λ a : Type,
|
||||||
(λ b : Type, if Bool ((λ x : Type, if Bool ((g a b) = (f x)) ⊥ ⊤) = (λ x : Type, ⊤)) ⊥ ⊤) =
|
(λ b : Type, if ((λ x : Type, if ((g a b) = (f x)) ⊥ ⊤) = (λ x : Type, ⊤)) ⊥ ⊤) = (λ x : Type, ⊤)) =
|
||||||
(λ x : Type, ⊤)) =
|
|
||||||
(λ x : Type, ⊤)
|
(λ x : Type, ⊤)
|
||||||
|
|
Loading…
Reference in a new issue