Zulip Chat Archive

Stream: new members

Topic: finmap inside an inductive type


Guilherme Espada (Mar 22 2021 at 15:21):

I'm trying to define an inductive type, of which one of the parameters is a finmap. Like this:

inductive ttype
| ty_func (l r:ttype): ttype
| ty_bool : ttype
| ty_record (m: finmap (λ(x:string),ttype)): ttype

Lean reports inductive type being declared can only be nested inside the parameters of other inductive types. How can I work around this limitation?

Guilherme Espada (Mar 23 2021 at 09:42):

I tried working around it by defining my own map-like structure:

@[derive decidable_eq]
inductive recordmap (T : Type u)
| nil : recordmap
| cons (k:string) (v : T) (tl : recordmap) : recordmap

@[derive decidable_eq]
inductive ttype
| ty_func (l r:ttype): ttype
| ty_bool : ttype
| ty_record (m: (recordmap ttype)): ttype

However, this errors out with:

tactic.mk_instance failed to generate instance for
  decidable_eq _nest_1_1.ttype

I definitely need decidable_eq. How can I fix this problem?

Kenny Lau (Mar 23 2021 at 09:52):

Looks like you would need to construct dec eq yourself

Guilherme Espada (Mar 23 2021 at 09:55):

Is there a document that details how to do so?

Horatiu Cheval (Mar 23 2021 at 10:07):

docs#decidable_eq, and more generally docs#decidable. In general, to prove p : Prop is decidable you need to provide a proof either for it or for its negation (as attested by the is_false and is_true constructors of decidable)

Kevin Buzzard (Mar 23 2021 at 10:07):

import tactic

@[derive decidable_eq]
inductive recordmap (T : Type u)
| nil : recordmap
| cons (k:string) (v : T) (tl : recordmap) : recordmap

inductive ttype
| ty_func (l r:ttype): ttype
| ty_bool : ttype
| ty_record (m: (recordmap ttype)): ttype

namespace ttype

instance : decidable_eq ttype
| (ty_func l r) ty_bool := is_false (by simp)
...

That sort of thing.

Guilherme Espada (Mar 23 2021 at 10:14):

Alright, that's very helpful thanks. Another question: how can I prevent library_search from cheating by importing classical?

Kevin Buzzard (Mar 23 2021 at 10:16):

I'm afraid that mathlib has made a design decision to be resolutely classical and you're not the first person to ask for it to be switched off but it's difficult.

Horatiu Cheval (Mar 23 2021 at 10:18):

But I noticed that following (I am not sure whether it's a problem or not)

@[derive decidable_eq]
inductive recordmap (T : Type u)
| nil : recordmap
| cons (k:string) (v : T) (tl : recordmap) : recordmap

#check @recordmap.decidable_eq
-- recordmap.decidable_eq : Π (T : Type u_1) [a : decidable_eq T], decidable_eq (recordmap T)
-- only records of type with decidable_eq have decidable_eq


-- @[derive decidable_eq]
inductive ttype
-- | ty_func (l r:ttype): ttype
-- | ty_bool : ttype
| ty_record (m: (recordmap ttype)): ttype

instance (a b : ttype) : decidable (a = b) :=
begin
  cases a, cases b,
  -- it seems you need decidable_eq for records of ttype,
  -- but for that you need to already know that ttype has decidable_eq, i.e. what we're trying to prove
  have := recordmap.decidable_eq ttype, --doesn't work
end

Is this right?

Kevin Buzzard (Mar 23 2021 at 10:19):

Why not do induction instead of cases?

Guilherme Espada (Mar 23 2021 at 10:21):

It seems to me that induction should work here

Guilherme Espada (Mar 23 2021 at 10:21):

but I'm just cheating with classical for now

Horatiu Cheval (Mar 23 2021 at 10:33):

Kevin Buzzard said:

Why not do induction instead of cases?

Well, it seems that for me it doesn't generate a hypothesis of induction in that case, so it's the same as cases:

instance (a b : ttype) : decidable (a = b) :=
begin
  induction a; induction b,
  case ty_record ty_record
  {
--     case ttype.ty_record ttype.ty_record
--     ab: recordmap ttype
--     ⊢ decidable (ttype.ty_record a = ttype.ty_record b)
  }
end

Horatiu Cheval (Mar 23 2021 at 10:35):

Am I missing the right induction?

Kevin Buzzard (Mar 23 2021 at 10:37):

Perhaps the problem with your simplification is that your simplified type is empty? Or maybe I'm wrong?

Horatiu Cheval (Mar 23 2021 at 10:39):

If you mean that previously I commented out the ty_func and ty_bool cases, I uncommented them before writing the snippet, and it's the same behavior.

Kevin Buzzard (Mar 23 2021 at 10:40):

I am not an expert on these induction matters, I'm afraid.

Horatiu Cheval (Mar 23 2021 at 10:45):

I also noticed that if I try

@[derive decidable_eq]
inductive ttype
| ty_func (l r:ttype): ttype
| ty_bool : ttype
| ty_record (m: (recordmap ttype)): ttype

then it fails with "'ttype' is not an inductive type", so maybe it has something to do with that.

Kevin Buzzard (Mar 23 2021 at 10:58):

I think that if you know what you're doing (and I don't) then it should be possible to prove this sort of thing by induction, perhaps with the equation compiler. It seems to me that you want to prove decidable equality of both ttype and recordmap ttype at once. The following failed attempt to prove decidable equality directly indicates this, at least: I'd be done if I also had a proof of decidable eq for the "simpler" recordmap terms. But as I say, I don't really know what the tricks are in this area, I'm a mathematician, I only do induction on nats.

instance (a b : ttype) : decidable (a = b) :=
begin
  induction a generalizing b,
  induction a generalizing b,
  { induction b,
    induction b,
    { apply is_true,
      refl },
    { apply is_false,
      simp } },
  induction b generalizing a_v a_tl,
  induction b generalizing a_v a_tl,
  { apply is_false,
    simp },
  -- now we're done if we can decide a_tl = b_tl
  sorry
end

Horatiu Cheval (Mar 23 2021 at 12:23):

Ok, I think I understand your point. Thanks for explaining the idea

Mario Carneiro (Mar 23 2021 at 23:17):

@Horatiu Cheval For these sorts of definitions, I always recommend not using nested inductives and instead rolling your own with a plain inductive type, which supports the correct induction principle

import logic.basic tactic.cache

inductive ttype_or_list : bool  Type
| ty_func (l r : ttype_or_list ff) : ttype_or_list ff
| ty_bool : ttype_or_list ff
| ty_record (m : ttype_or_list tt) : ttype_or_list ff
| tyl_nil : ttype_or_list tt
| tyl_cons (k:string) (v : ttype_or_list ff) (tl : ttype_or_list tt) : ttype_or_list tt

def ttype := ttype_or_list ff

instance {c} : decidable_eq (ttype_or_list c) :=
begin
  intros a b,
  induction a with al ar IH1 IH2 a _ ak av al generalizing b; cases b,
  case ty_func ty_func : bl br { exactI decidable_of_iff' (al = bl  ar = br) (by simp) },
  case ty_bool ty_bool { exact decidable.is_true rfl },
  case ty_record ty_record : b { exactI decidable_of_iff' (a = b) (by simp) },
  case tyl_nil tyl_nil { exact decidable.is_true rfl },
  case tyl_cons tyl_cons : bk bv bl {
    exactI decidable_of_iff' (ak = bk  av = bv  al = bl) (by simp) },
  all_goals { apply decidable.is_false, simp },
end

instance : decidable_eq ttype := ttype_or_list.decidable_eq

Horatiu Cheval (Mar 24 2021 at 18:20):

Thanks for working out the example. Though the initial question was asked by @Guilherme Espada, it will certainly be, for me too, a good future reference on how to rearrange nested inductives


Last updated: Dec 20 2023 at 11:08 UTC