parent
d591c63840
commit
d731a4ab13
9 changed files with 147 additions and 44 deletions
|
@ -118,7 +118,7 @@
|
||||||
(,(rx symbol-start "_" symbol-end) . 'font-lock-preprocessor-face)
|
(,(rx symbol-start "_" symbol-end) . 'font-lock-preprocessor-face)
|
||||||
;; modifiers
|
;; modifiers
|
||||||
(,(rx (or "\[persistent\]" "\[notation\]" "\[visible\]" "\[instance\]" "\[class\]" "\[parsing-only\]"
|
(,(rx (or "\[persistent\]" "\[notation\]" "\[visible\]" "\[instance\]" "\[class\]" "\[parsing-only\]"
|
||||||
"\[coercion\]" "\[reducible\]" "\[irreducible\]" "\[semireducible\]" "\[quasireducible\]" "\[wf\]"
|
"\[coercion\]" "\[unfold-f\]" "\[reducible\]" "\[irreducible\]" "\[semireducible\]" "\[quasireducible\]" "\[wf\]"
|
||||||
"\[whnf\]" "\[multiple-instances\]" "\[none\]"
|
"\[whnf\]" "\[multiple-instances\]" "\[none\]"
|
||||||
"\[decls\]" "\[declarations\]" "\[all-transparent\]" "\[coercions\]" "\[classes\]"
|
"\[decls\]" "\[declarations\]" "\[all-transparent\]" "\[coercions\]" "\[classes\]"
|
||||||
"\[notations\]" "\[abbreviations\]" "\[begin-end-hints\]" "\[tactic-hints\]" "\[reduce-hints\]"))
|
"\[notations\]" "\[abbreviations\]" "\[begin-end-hints\]" "\[tactic-hints\]" "\[reduce-hints\]"))
|
||||||
|
|
|
@ -300,6 +300,7 @@ struct decl_attributes {
|
||||||
bool m_is_class;
|
bool m_is_class;
|
||||||
bool m_is_parsing_only;
|
bool m_is_parsing_only;
|
||||||
bool m_has_multiple_instances;
|
bool m_has_multiple_instances;
|
||||||
|
bool m_unfold_f_hint;
|
||||||
optional<unsigned> m_priority;
|
optional<unsigned> m_priority;
|
||||||
optional<unsigned> m_unfold_c_hint;
|
optional<unsigned> m_unfold_c_hint;
|
||||||
|
|
||||||
|
@ -317,6 +318,7 @@ struct decl_attributes {
|
||||||
m_is_class = false;
|
m_is_class = false;
|
||||||
m_is_parsing_only = false;
|
m_is_parsing_only = false;
|
||||||
m_has_multiple_instances = false;
|
m_has_multiple_instances = false;
|
||||||
|
m_unfold_f_hint = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct elim_choice_fn : public replace_visitor {
|
struct elim_choice_fn : public replace_visitor {
|
||||||
|
@ -421,6 +423,9 @@ struct decl_attributes {
|
||||||
"marked as '[parsing-only]'", pos);
|
"marked as '[parsing-only]'", pos);
|
||||||
m_is_parsing_only = true;
|
m_is_parsing_only = true;
|
||||||
p.next();
|
p.next();
|
||||||
|
} else if (p.curr_is_token(get_unfold_f_tk())) {
|
||||||
|
p.next();
|
||||||
|
m_unfold_f_hint = true;
|
||||||
} else if (p.curr_is_token(get_unfold_c_tk())) {
|
} else if (p.curr_is_token(get_unfold_c_tk())) {
|
||||||
p.next();
|
p.next();
|
||||||
unsigned r = p.parse_small_nat();
|
unsigned r = p.parse_small_nat();
|
||||||
|
@ -469,7 +474,9 @@ struct decl_attributes {
|
||||||
if (m_is_quasireducible)
|
if (m_is_quasireducible)
|
||||||
env = set_reducible(env, d, reducible_status::Quasireducible, m_persistent);
|
env = set_reducible(env, d, reducible_status::Quasireducible, m_persistent);
|
||||||
if (m_unfold_c_hint)
|
if (m_unfold_c_hint)
|
||||||
env = add_unfold_c_hint(env, d, m_unfold_c_hint, m_persistent);
|
env = add_unfold_c_hint(env, d, *m_unfold_c_hint, m_persistent);
|
||||||
|
if (m_unfold_f_hint)
|
||||||
|
env = add_unfold_f_hint(env, d, m_persistent);
|
||||||
}
|
}
|
||||||
if (m_is_class)
|
if (m_is_class)
|
||||||
env = add_class(env, d, m_persistent);
|
env = add_class(env, d, m_persistent);
|
||||||
|
|
|
@ -93,7 +93,7 @@ void init_token_table(token_table & t) {
|
||||||
"variables", "parameter", "parameters", "constant", "constants", "[persistent]", "[visible]", "[instance]",
|
"variables", "parameter", "parameters", "constant", "constants", "[persistent]", "[visible]", "[instance]",
|
||||||
"[none]", "[class]", "[coercion]", "[reducible]", "[irreducible]", "[semireducible]", "[quasireducible]",
|
"[none]", "[class]", "[coercion]", "[reducible]", "[irreducible]", "[semireducible]", "[quasireducible]",
|
||||||
"[parsing-only]", "[multiple-instances]",
|
"[parsing-only]", "[multiple-instances]",
|
||||||
"evaluate", "check", "eval", "[wf]", "[whnf]", "[all-transparent]", "[priority", "[unfold-c", "print",
|
"evaluate", "check", "eval", "[wf]", "[whnf]", "[all-transparent]", "[priority", "[unfold-f]", "[unfold-c", "print",
|
||||||
"end", "namespace", "section", "prelude", "help",
|
"end", "namespace", "section", "prelude", "help",
|
||||||
"import", "inductive", "record", "structure", "module", "universe", "universes", "local",
|
"import", "inductive", "record", "structure", "module", "universe", "universes", "local",
|
||||||
"precedence", "reserve", "infixl", "infixr", "infix", "postfix", "prefix", "notation", "context",
|
"precedence", "reserve", "infixl", "infixr", "infix", "postfix", "prefix", "notation", "context",
|
||||||
|
|
|
@ -102,6 +102,7 @@ static name * g_opaque = nullptr;
|
||||||
static name * g_instance = nullptr;
|
static name * g_instance = nullptr;
|
||||||
static name * g_priority = nullptr;
|
static name * g_priority = nullptr;
|
||||||
static name * g_unfold_c = nullptr;
|
static name * g_unfold_c = nullptr;
|
||||||
|
static name * g_unfold_f = nullptr;
|
||||||
static name * g_coercion = nullptr;
|
static name * g_coercion = nullptr;
|
||||||
static name * g_reducible = nullptr;
|
static name * g_reducible = nullptr;
|
||||||
static name * g_quasireducible = nullptr;
|
static name * g_quasireducible = nullptr;
|
||||||
|
@ -227,6 +228,7 @@ void initialize_tokens() {
|
||||||
g_instance = new name("[instance]");
|
g_instance = new name("[instance]");
|
||||||
g_priority = new name("[priority");
|
g_priority = new name("[priority");
|
||||||
g_unfold_c = new name("[unfold-c");
|
g_unfold_c = new name("[unfold-c");
|
||||||
|
g_unfold_f = new name("[unfold-f]");
|
||||||
g_coercion = new name("[coercion]");
|
g_coercion = new name("[coercion]");
|
||||||
g_reducible = new name("[reducible]");
|
g_reducible = new name("[reducible]");
|
||||||
g_quasireducible = new name("[quasireducible]");
|
g_quasireducible = new name("[quasireducible]");
|
||||||
|
@ -290,6 +292,7 @@ void finalize_tokens() {
|
||||||
delete g_instance;
|
delete g_instance;
|
||||||
delete g_priority;
|
delete g_priority;
|
||||||
delete g_unfold_c;
|
delete g_unfold_c;
|
||||||
|
delete g_unfold_f;
|
||||||
delete g_coercion;
|
delete g_coercion;
|
||||||
delete g_reducible;
|
delete g_reducible;
|
||||||
delete g_quasireducible;
|
delete g_quasireducible;
|
||||||
|
@ -479,6 +482,7 @@ name const & get_opaque_tk() { return *g_opaque; }
|
||||||
name const & get_instance_tk() { return *g_instance; }
|
name const & get_instance_tk() { return *g_instance; }
|
||||||
name const & get_priority_tk() { return *g_priority; }
|
name const & get_priority_tk() { return *g_priority; }
|
||||||
name const & get_unfold_c_tk() { return *g_unfold_c; }
|
name const & get_unfold_c_tk() { return *g_unfold_c; }
|
||||||
|
name const & get_unfold_f_tk() { return *g_unfold_f; }
|
||||||
name const & get_coercion_tk() { return *g_coercion; }
|
name const & get_coercion_tk() { return *g_coercion; }
|
||||||
name const & get_reducible_tk() { return *g_reducible; }
|
name const & get_reducible_tk() { return *g_reducible; }
|
||||||
name const & get_quasireducible_tk() { return *g_quasireducible; }
|
name const & get_quasireducible_tk() { return *g_quasireducible; }
|
||||||
|
|
|
@ -104,6 +104,7 @@ name const & get_opaque_tk();
|
||||||
name const & get_instance_tk();
|
name const & get_instance_tk();
|
||||||
name const & get_priority_tk();
|
name const & get_priority_tk();
|
||||||
name const & get_unfold_c_tk();
|
name const & get_unfold_c_tk();
|
||||||
|
name const & get_unfold_f_tk();
|
||||||
name const & get_coercion_tk();
|
name const & get_coercion_tk();
|
||||||
name const & get_reducible_tk();
|
name const & get_reducible_tk();
|
||||||
name const & get_semireducible_tk();
|
name const & get_semireducible_tk();
|
||||||
|
|
|
@ -19,42 +19,64 @@ Author: Leonardo de Moura
|
||||||
|
|
||||||
namespace lean {
|
namespace lean {
|
||||||
/**
|
/**
|
||||||
\brief c_unfold hint instructs the normalizer (and simplifier) that
|
\brief unfold hints instruct the normalizer (and simplifier) that
|
||||||
a function application (f a_1 ... a_i ... a_n) should be unfolded
|
a function application. We have two kinds of hints:
|
||||||
when argument a_i is a constructor.
|
- unfold_c (f a_1 ... a_i ... a_n) should be unfolded
|
||||||
|
when argument a_i is a constructor.
|
||||||
|
- unfold_f (f a_1 ... a_i ... a_n) should be unfolded when it is fully applied.
|
||||||
*/
|
*/
|
||||||
struct unfold_c_hint_entry {
|
struct unfold_hint_entry {
|
||||||
name m_decl_name;
|
bool m_unfold_c; //!< true if it is an unfold_c hint
|
||||||
optional<unsigned> m_arg_idx;
|
bool m_add; //!< add/remove hint
|
||||||
unfold_c_hint_entry() {}
|
name m_decl_name;
|
||||||
unfold_c_hint_entry(name const & n, optional<unsigned> const & idx):m_decl_name(n), m_arg_idx(idx) {}
|
unsigned m_arg_idx;
|
||||||
|
unfold_hint_entry():m_unfold_c(false), m_add(false), m_arg_idx(0) {}
|
||||||
|
unfold_hint_entry(bool unfold_c, bool add, name const & n, unsigned idx):
|
||||||
|
m_unfold_c(unfold_c), m_add(add), m_decl_name(n), m_arg_idx(idx) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
static name * g_unfold_c_hint_name = nullptr;
|
unfold_hint_entry mk_add_unfold_c_entry(name const & n, unsigned idx) { return unfold_hint_entry(true, true, n, idx); }
|
||||||
|
unfold_hint_entry mk_erase_unfold_c_entry(name const & n) { return unfold_hint_entry(true, false, n, 0); }
|
||||||
|
unfold_hint_entry mk_add_unfold_f_entry(name const & n) { return unfold_hint_entry(false, true, n, 0); }
|
||||||
|
unfold_hint_entry mk_erase_unfold_f_entry(name const & n) { return unfold_hint_entry(false, false, n, 0); }
|
||||||
|
|
||||||
|
static name * g_unfold_hint_name = nullptr;
|
||||||
static std::string * g_key = nullptr;
|
static std::string * g_key = nullptr;
|
||||||
|
|
||||||
struct unfold_c_hint_config {
|
struct unfold_hint_state {
|
||||||
typedef name_map<unsigned> state;
|
name_map<unsigned> m_unfold_c;
|
||||||
typedef unfold_c_hint_entry entry;
|
name_set m_unfold_f;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct unfold_hint_config {
|
||||||
|
typedef unfold_hint_state state;
|
||||||
|
typedef unfold_hint_entry entry;
|
||||||
|
|
||||||
static void add_entry(environment const &, io_state const &, state & s, entry const & e) {
|
static void add_entry(environment const &, io_state const &, state & s, entry const & e) {
|
||||||
if (e.m_arg_idx)
|
if (e.m_unfold_c) {
|
||||||
s.insert(e.m_decl_name, *e.m_arg_idx);
|
if (e.m_add)
|
||||||
else
|
s.m_unfold_c.insert(e.m_decl_name, e.m_arg_idx);
|
||||||
s.erase(e.m_decl_name);
|
else
|
||||||
|
s.m_unfold_c.erase(e.m_decl_name);
|
||||||
|
} else {
|
||||||
|
if (e.m_add)
|
||||||
|
s.m_unfold_f.insert(e.m_decl_name);
|
||||||
|
else
|
||||||
|
s.m_unfold_f.erase(e.m_decl_name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
static name const & get_class_name() {
|
static name const & get_class_name() {
|
||||||
return *g_unfold_c_hint_name;
|
return *g_unfold_hint_name;
|
||||||
}
|
}
|
||||||
static std::string const & get_serialization_key() {
|
static std::string const & get_serialization_key() {
|
||||||
return *g_key;
|
return *g_key;
|
||||||
}
|
}
|
||||||
static void write_entry(serializer & s, entry const & e) {
|
static void write_entry(serializer & s, entry const & e) {
|
||||||
s << e.m_decl_name << e.m_arg_idx;
|
s << e.m_unfold_c << e.m_add << e.m_decl_name << e.m_arg_idx;
|
||||||
}
|
}
|
||||||
static entry read_entry(deserializer & d) {
|
static entry read_entry(deserializer & d) {
|
||||||
entry e;
|
entry e;
|
||||||
d >> e.m_decl_name >> e.m_arg_idx;
|
d >> e.m_unfold_c >> e.m_add >> e.m_decl_name >> e.m_arg_idx;
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
static optional<unsigned> get_fingerprint(entry const & e) {
|
static optional<unsigned> get_fingerprint(entry const & e) {
|
||||||
|
@ -62,33 +84,53 @@ struct unfold_c_hint_config {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template class scoped_ext<unfold_c_hint_config>;
|
template class scoped_ext<unfold_hint_config>;
|
||||||
typedef scoped_ext<unfold_c_hint_config> unfold_c_hint_ext;
|
typedef scoped_ext<unfold_hint_config> unfold_hint_ext;
|
||||||
|
|
||||||
environment add_unfold_c_hint(environment const & env, name const & n, optional<unsigned> idx, bool persistent) {
|
environment add_unfold_c_hint(environment const & env, name const & n, unsigned idx, bool persistent) {
|
||||||
declaration const & d = env.get(n);
|
declaration const & d = env.get(n);
|
||||||
if (!d.is_definition() || d.is_opaque())
|
if (!d.is_definition() || d.is_opaque())
|
||||||
throw exception("invalid unfold-c hint, declaration must be a non-opaque definition");
|
throw exception("invalid unfold-c hint, declaration must be a non-opaque definition");
|
||||||
return unfold_c_hint_ext::add_entry(env, get_dummy_ios(), unfold_c_hint_entry(n, idx), persistent);
|
return unfold_hint_ext::add_entry(env, get_dummy_ios(), mk_add_unfold_c_entry(n, idx), persistent);
|
||||||
}
|
}
|
||||||
|
|
||||||
optional<unsigned> has_unfold_c_hint(environment const & env, name const & d) {
|
optional<unsigned> has_unfold_c_hint(environment const & env, name const & d) {
|
||||||
name_map<unsigned> const & s = unfold_c_hint_ext::get_state(env);
|
unfold_hint_state const & s = unfold_hint_ext::get_state(env);
|
||||||
if (auto it = s.find(d))
|
if (auto it = s.m_unfold_c.find(d))
|
||||||
return optional<unsigned>(*it);
|
return optional<unsigned>(*it);
|
||||||
else
|
else
|
||||||
return optional<unsigned>();
|
return optional<unsigned>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
environment erase_unfold_c_hint(environment const & env, name const & n, bool persistent) {
|
||||||
|
return unfold_hint_ext::add_entry(env, get_dummy_ios(), mk_erase_unfold_c_entry(n), persistent);
|
||||||
|
}
|
||||||
|
|
||||||
|
environment add_unfold_f_hint(environment const & env, name const & n, bool persistent) {
|
||||||
|
declaration const & d = env.get(n);
|
||||||
|
if (!d.is_definition() || d.is_opaque())
|
||||||
|
throw exception("invalid unfold-f hint, declaration must be a non-opaque definition");
|
||||||
|
return unfold_hint_ext::add_entry(env, get_dummy_ios(), mk_add_unfold_f_entry(n), persistent);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has_unfold_f_hint(environment const & env, name const & d) {
|
||||||
|
unfold_hint_state const & s = unfold_hint_ext::get_state(env);
|
||||||
|
return s.m_unfold_f.contains(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
environment erase_unfold_f_hint(environment const & env, name const & n, bool persistent) {
|
||||||
|
return unfold_hint_ext::add_entry(env, get_dummy_ios(), mk_erase_unfold_f_entry(n), persistent);
|
||||||
|
}
|
||||||
|
|
||||||
void initialize_normalize() {
|
void initialize_normalize() {
|
||||||
g_unfold_c_hint_name = new name("c-unfold");
|
g_unfold_hint_name = new name("unfold-hints");
|
||||||
g_key = new std::string("c-unfold");
|
g_key = new std::string("unfoldh");
|
||||||
unfold_c_hint_ext::initialize();
|
unfold_hint_ext::initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
void finalize_normalize() {
|
void finalize_normalize() {
|
||||||
unfold_c_hint_ext::finalize();
|
unfold_hint_ext::finalize();
|
||||||
delete g_unfold_c_hint_name;
|
delete g_unfold_hint_name;
|
||||||
delete g_key;
|
delete g_key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,6 +155,10 @@ class normalize_fn {
|
||||||
return ::lean::has_unfold_c_hint(m_tc.env(), const_name(f));
|
return ::lean::has_unfold_c_hint(m_tc.env(), const_name(f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool has_unfold_f_hint(expr const & f) {
|
||||||
|
return is_constant(f) && ::lean::has_unfold_f_hint(m_tc.env(), const_name(f));
|
||||||
|
}
|
||||||
|
|
||||||
expr normalize_app(expr const & e) {
|
expr normalize_app(expr const & e) {
|
||||||
buffer<expr> args;
|
buffer<expr> args;
|
||||||
bool modified = false;
|
bool modified = false;
|
||||||
|
@ -123,6 +169,12 @@ class normalize_fn {
|
||||||
modified = true;
|
modified = true;
|
||||||
a = new_a;
|
a = new_a;
|
||||||
}
|
}
|
||||||
|
if (has_unfold_f_hint(f)) {
|
||||||
|
if (!is_pi(m_tc.whnf(m_tc.infer(e).first).first)) {
|
||||||
|
if (optional<expr> r = unfold_app(m_tc.env(), mk_rev_app(f, args)))
|
||||||
|
return normalize(*r);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (auto idx = has_unfold_c_hint(f)) {
|
if (auto idx = has_unfold_c_hint(f)) {
|
||||||
if (*idx < args.size() && is_constructor_app(m_tc.env(), args[args.size() - *idx - 1])) {
|
if (*idx < args.size() && is_constructor_app(m_tc.env(), args[args.size() - *idx - 1])) {
|
||||||
if (optional<expr> r = unfold_app(m_tc.env(), mk_rev_app(f, args)))
|
if (optional<expr> r = unfold_app(m_tc.env(), mk_rev_app(f, args)))
|
||||||
|
|
|
@ -32,27 +32,26 @@ expr normalize(type_checker & tc, expr const & e, constraint_seq & cs, bool eta
|
||||||
expr normalize(type_checker & tc, expr const & e, std::function<bool(expr const&)> const & pred, // NOLINT
|
expr normalize(type_checker & tc, expr const & e, std::function<bool(expr const&)> const & pred, // NOLINT
|
||||||
constraint_seq & cs, bool eta = false);
|
constraint_seq & cs, bool eta = false);
|
||||||
|
|
||||||
/** \brief c_unfold hint instructs the normalizer (and simplifier) that
|
/** \brief unfold-c hint instructs the normalizer (and simplifier) that
|
||||||
a function application (f a_1 ... a_i ... a_n) should be unfolded
|
a function application (f a_1 ... a_i ... a_n) should be unfolded
|
||||||
when argument a_i is a constructor.
|
when argument a_i is a constructor.
|
||||||
|
|
||||||
The constant will be unfolded even if it the whnf procedure did not unfolded it.
|
The constant will be unfolded even if it the whnf procedure did not unfolded it.
|
||||||
|
|
||||||
Of course, kernel opaque constants are not unfolded.
|
Of course, kernel opaque constants are not unfolded.
|
||||||
|
|
||||||
\remark If idx is none, then the hint is removed.
|
|
||||||
*/
|
*/
|
||||||
environment add_unfold_c_hint(environment const & env, name const & n, optional<unsigned> idx, bool persistent = true);
|
environment add_unfold_c_hint(environment const & env, name const & n, unsigned idx, bool persistent = true);
|
||||||
inline environment add_unfold_c_hint(environment const & env, name const & n, unsigned idx, bool persistent = true) {
|
environment erase_unfold_c_hint(environment const & env, name const & n, bool persistent = true);
|
||||||
return add_unfold_c_hint(env, n, optional<unsigned>(idx), persistent);
|
|
||||||
}
|
|
||||||
inline environment erase_unfold_c_hint(environment const & env, name const & n, bool persistent = true) {
|
|
||||||
return add_unfold_c_hint(env, n, optional<unsigned>(), persistent);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** \brief Retrieve the hint added with the procedure add_unfold_c_hint. */
|
/** \brief Retrieve the hint added with the procedure add_unfold_c_hint. */
|
||||||
optional<unsigned> has_unfold_c_hint(environment const & env, name const & d);
|
optional<unsigned> has_unfold_c_hint(environment const & env, name const & d);
|
||||||
|
|
||||||
|
/** \brief unfold-f hint instructs normalizer (and simplifier) that function application
|
||||||
|
(f a_1 ... a_n) should be unfolded when it is fully applied */
|
||||||
|
environment add_unfold_f_hint(environment const & env, name const & n, bool persistent = true);
|
||||||
|
environment erase_unfold_f_hint(environment const & env, name const & n, bool persistent = true);
|
||||||
|
/** \brief Retrieve the hint added with the procedure add_unfold_f_hint. */
|
||||||
|
optional<unsigned> has_unfold_f_hint(environment const & env, name const & d);
|
||||||
|
|
||||||
void initialize_normalize();
|
void initialize_normalize();
|
||||||
void finalize_normalize();
|
void finalize_normalize();
|
||||||
}
|
}
|
||||||
|
|
28
tests/lean/unfoldf.lean
Normal file
28
tests/lean/unfoldf.lean
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
open nat
|
||||||
|
|
||||||
|
definition id [unfold-f] {A : Type} (a : A) := a
|
||||||
|
definition compose {A B C : Type} (g : B → C) (f : A → B) (a : A) := g (f a)
|
||||||
|
notation g ∘ f := compose g f
|
||||||
|
|
||||||
|
example (a b : nat) (H : a = b) : id a = b :=
|
||||||
|
begin
|
||||||
|
esimp,
|
||||||
|
state,
|
||||||
|
exact H
|
||||||
|
end
|
||||||
|
|
||||||
|
example (a b : nat) (H : a = b) : (id ∘ id) a = b :=
|
||||||
|
begin
|
||||||
|
esimp,
|
||||||
|
state,
|
||||||
|
exact H
|
||||||
|
end
|
||||||
|
|
||||||
|
attribute compose [unfold-f]
|
||||||
|
|
||||||
|
example (a b : nat) (H : a = b) : (id ∘ id) a = b :=
|
||||||
|
begin
|
||||||
|
esimp,
|
||||||
|
state,
|
||||||
|
exact H
|
||||||
|
end
|
12
tests/lean/unfoldf.lean.expected.out
Normal file
12
tests/lean/unfoldf.lean.expected.out
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
unfoldf.lean:10:2: proof state
|
||||||
|
a b : ℕ,
|
||||||
|
H : a = b
|
||||||
|
⊢ a = b
|
||||||
|
unfoldf.lean:17:2: proof state
|
||||||
|
a b : ℕ,
|
||||||
|
H : a = b
|
||||||
|
⊢ (id ∘ id) a = b
|
||||||
|
unfoldf.lean:26:2: proof state
|
||||||
|
a b : ℕ,
|
||||||
|
H : a = b
|
||||||
|
⊢ a = b
|
Loading…
Reference in a new issue