Zulip Chat Archive
Stream: lean4
Topic: Defining mutable rose trees
Jannis Limperg (Jun 03 2021 at 11:00):
I'm trying to define a data structure that, in slightly simplified form, amounts to a mutable rose tree, where nodes carry references to their children and a reference back to their parent. I've found a way to do it (see below), but it's a bit cumbersome. Is there a more elegant way?
My construction:
unsafe inductive MutRoseTreeImp (α : Type) : Type
| mk
(parent : Option (IO.Ref (MutRoseTreeImp α)))
(children : Array (IO.Ref (MutRoseTreeImp α)))
(payload : α)
structure MutRoseTreeSpec (α : Type) where
MutRoseTree : Type
construct :
Option (IO.Ref MutRoseTree) →
Array (IO.Ref MutRoseTree) →
α →
MutRoseTree
parent : MutRoseTree → Option (IO.Ref MutRoseTree)
children : MutRoseTree → Array (IO.Ref MutRoseTree)
payload : MutRoseTree → α
-- We could add equations relating `construct` and the field accessors.
open MutRoseTreeImp in
unsafe def MutRoseTreeSpecImp α : MutRoseTreeSpec α where
MutRoseTree := MutRoseTreeImp α
construct := MutRoseTreeImp.mk
parent | (mk x _ _) => x
children | (mk _ x _) => x
payload | (mk _ _ x) => x
@[implementedBy MutRoseTreeSpecImp]
constant mutRoseTreeSpec α : MutRoseTreeSpec α := {
MutRoseTree := α
construct := fun _ _ a => a
parent := fun _ => arbitrary
children := fun _ => arbitrary
payload := fun a => a
}
def MutRoseTree α :=
(mutRoseTreeSpec α).MutRoseTree
namespace MutRoseTree
variable {α : Type}
def mk := (mutRoseTreeSpec α).construct
def parent := (mutRoseTreeSpec α).parent
def children := (mutRoseTreeSpec α).children
def payload := (mutRoseTreeSpec α).payload
end MutRoseTree
Some questions you might have:
- Why is
MutRoseTreeImp
unsafe in the first place? Because apparently the kernel doesn't know that IORefs are strictly positive, so without theunsafe
it complains about an invalid occurrence ofMutRoseTreeImp
in a constructor argument. - Can you generalise to
STRef
? No, this gives error "(kernel) mutually inductive types must live in the same universe". This seems like a bug. I imagine Lean tries to compile the data type as a nested type and introduces some universe issue during that. (But why doesn't the same happen withIORef
?) - Why do you need the type to be non-
unsafe
in the first place? I don't really, this is for a tactic so I guess I could just leave everything unsafe. But where's the fun in that?
Mario Carneiro (Jun 03 2021 at 11:37):
Regarding Q2:
abbrev Ref := ST.Ref
unsafe inductive MutRoseTreeImp (σ α : Type) : Type
| mk
(parent : Option (Ref σ (MutRoseTreeImp σ α)))
(children : Array (Ref σ (MutRoseTreeImp σ α)))
(payload : α)
Mario Carneiro (Jun 03 2021 at 11:37):
very strange
Jannis Limperg (Jun 03 2021 at 11:39):
Ah, that's a nice workaround for now. Thanks!
Mario Carneiro (Jun 03 2021 at 11:58):
I'm not sure it's a good idea, but you can use the type unsafety mechanism I just posted about in order to avoid unsafe
here, by storing effectively void*
pointers in the inductive (which means it doesn't need to be recursive) and casting them to rose tree pointers for the API
Jannis Limperg (Jun 03 2021 at 12:13):
Evil (but delightful?). This would make the construction a lot shorter, but I guess the STRefs will become opaque at some point so I won't rely on it.
Last updated: Dec 20 2023 at 11:08 UTC