Zulip Chat Archive

Stream: lean4

Topic: instance with tactic autoparam


James Gallicchio (Mar 19 2023 at 18:07):

is there a way to get something like this working?

instance (h : n < UInt8.size := by decide) : CoeDep Char (Char.ofNat n) UInt8 where
  coe := n, h

#check (instCoeDepCharOfNatUInt8 : CoeDep Char 'a' UInt8)
#synth CoeDep Char 'a' UInt8

the synth line fails unfortunately

James Gallicchio (Mar 19 2023 at 18:07):

Oh wait, meant for this to be in #lean4 if someone is able to move it >_<

Notification Bot (Mar 19 2023 at 18:13):

This topic was moved here from #mathlib4 > instance with tactic autoparam by Eric Wieser.

Alex Keizer (Mar 19 2023 at 19:11):

I don't think you can use decide like that, but you can trick the typeclass synthesis system into proving such inequalities. E.g., the following is part of mathlib (https://github.com/leanprover-community/mathlib4/blob/master/Mathlib/Data/Fin/Fin2.lean)

/-- This is a simple type class inference prover for proof obligations
  of the form `m < n` where `m n : ℕ`. -/
class IsLT (m n : ) where
  /-- The unique field of `Fin2.IsLT`, a proof that `m < n`. -/
  h : m < n
#align fin2.is_lt Fin2.IsLT

instance IsLT.zero (n) : IsLT 0 (succ n) :=
  succ_pos _

instance IsLT.succ (m n) [l : IsLT m n] : IsLT (succ m) (succ n) :=
  succ_lt_succ l.h

/-- Use type class inference to infer the boundedness proof, so that we can directly convert a
`Nat` into a `Fin2 n`. This supports notation like `&1 : Fin 3`. -/
def ofNat' :  {n} (m) [IsLT m n], Fin2 n
  | 0, _, h => absurd h (Nat.not_lt_zero _)
  | succ _, 0, _ => fz
  | succ n, succ m, h => fs (@ofNat' n m lt_of_succ_lt_succ h⟩)
#align fin2.of_nat' Fin2.ofNat'

James Gallicchio (Mar 19 2023 at 19:20):

I'm a bit worried doing that, just because chars get kind of big sometimes and I don't want tc synthesis to have to synthesize a chain of 200 elements instead of calling a dedicated tactic :/

James Gallicchio (Mar 19 2023 at 19:20):

maybe this is a feature request, heh

Eric Wieser (Mar 19 2023 at 20:41):

You could generate 256 instances instead for a chain length of 1

Alex Keizer (Mar 20 2023 at 11:01):

Being able to use autoparams for effectively running custom code as part of typeclass synthesis would be a very useful and powerful feature for sure.

Eric Wieser (Mar 20 2023 at 11:02):

Eric Wieser said:

You could generate 256 instances instead for a chain length of 1

Given you can do this with a macro and write only a handful of lines of code, I think this is a perfectly reasonable hack for UInt8; though I wouldn't advise it for Uint16

Gabriel Ebner (Mar 21 2023 at 18:57):

You can do this in a roundabout way:

class Decide (p : Prop) [Decidable p] : Prop where
  isTrue : p

instance : @Decide p (.isTrue h) := @Decide.mk p (_) h

instance [Decide (n < UInt8.size)] : CoeDep Char (Char.ofNat n) UInt8 where
  coe := n, Decide.isTrue

#check ('a' : UInt8)

Eric Wieser (Mar 21 2023 at 19:01):

How much reduction does this perform onDecidable instances to work out if they match isTrue?

Gabriel Ebner (Mar 21 2023 at 19:09):

Decidable p is an instance-implicit argument, so it is unified with semireducible transparency.

Gabriel Ebner (Mar 21 2023 at 19:10):

It's essentially the same thing that by decide does under the hood.

James Gallicchio (Mar 21 2023 at 21:46):

ah, very cool, that should work for my purposes

Sebastian Ullrich (Mar 22 2023 at 08:55):

James Gallicchio said:

is there a way to get something like this working?

The short answer from an implementation view is: the mutual recursion between typeclass synthesis and unification is already one of the most complicated parts of the system. Adding tactic execution to the mix would make the complexity explode, and likely complicate caching as well.

James Gallicchio (Mar 22 2023 at 18:28):

It seems very reasonable to keep it limited to decide esque problems -- anything beyond that feels like too much to expect tc synthesis to do. Maybe we can add the Decide class to Std.

Mario Carneiro (Mar 22 2023 at 23:54):

I still think that we should just allow tactic execution there, but require of implementers that the tactic must be pure, because the result will be cached just like a typeclass resolution problem

Mario Carneiro (Mar 22 2023 at 23:55):

Disallowing tactics just causes people to implement execution more poorly using the typeclass system. There are many cases where you can do significantly better with a custom automation

Alex Keizer (Mar 23 2023 at 14:05):

For example, my project often has typeclass problems of the form \all (i : Fin 3), Foo (f i), (where the 3 might be any concrete, known number) which I want to automatically be inferred from instances of Foo (f 0), Foo (f 1) and Foo (f 2). This is doable by having a chain of instances, effectively doing induction on the Fin (but even then I had to add an auxiliary typeclass, or the synthesis would somehow fail).
I would love to just say something like by intro i; fin_cases i; inferInstance


Last updated: Dec 20 2023 at 11:08 UTC