Zulip Chat Archive

Stream: lean4

Topic: Typeclass resolution with type functions


Juan Pablo Romero (Oct 17 2022 at 00:51):

I have a function that removes a trailing Unit on tuples:

class Simplify (A : Type) where
  apply: Type

instance : Simplify (A × B × Unit) := A × B
instance : Simplify (A × Unit) := A
instance : Simplify Unit := Unit

example : Simplify.apply Unit = Unit := rfl
example : Simplify.apply (Int × Unit) = Int := rfl
example : Simplify.apply (String × Int × Unit) = (String × Int) := rfl

But I'm having problems using it when the given type is calculated by another function:

def ArgTypes (s : String) : Type := toType s.toList
  where
    toType : List Char -> Type
      | [] => Unit
      | '%' :: 'd' :: rest => Int × toType rest
      | '%' :: 's' :: rest => String × toType rest
      | _ :: rest => toType rest

example : ArgTypes "abc" = Unit := rfl
example : ArgTypes "%s is %d" = (String × Int × Unit) := rfl
example : ArgTypes "%s is %d - %d" = (String × Int × Int × Unit) := rfl

#check Simplify.apply (ArgTypes "abc")

-- failed to synthesize instance
--  Simplify (ArgTypes "abc")

Looks like the resolution mechanism does not evaluate the argument?

Marcus Rossel (Oct 17 2022 at 05:05):

(deleted)

Floris van Doorn (Oct 17 2022 at 08:09):

Correct, type classes don't unfold (non-reducible) definitions.
This is something that is probably better to do with metaprogramming.
What is your use case?

Tomas Skrivan (Oct 17 2022 at 09:27):

The typeclass inference does not see through function marked with def, they need to be transparent i.e. marked with abbrev or with @[reducible].

Here is a simple example for creating nth power of a type TypePower α n = α × ... × α

abbrev TypePower (α : Type) : Nat  Type
  | 0 => Unit
  | 1 => α
  | n+2 => α × TypePower α (n+1)

def TypePower' (α : Type) : Nat  Type
  | 0 => Unit
  | 1 => α
  | n+2 => α × TypePower' α (n+1)

example : ToString (TypePower Nat 2) := by infer_instance  -- succeeds
example : ToString (TypePower' Nat 2) := by infer_instance -- fails

Tomas Skrivan (Oct 17 2022 at 09:31):

Here is a modification of your example that works:

class Simplify (A : Type) where
  apply: Type

instance : Simplify (A × B × Unit) := A × B
instance : Simplify (A × Unit) := A
instance : Simplify Unit := Unit

example : Simplify.apply Unit = Unit := rfl
example : Simplify.apply (Int × Unit) = Int := rfl
example : Simplify.apply (String × Int × Unit) = (String × Int) := rfl

def ArgTypes (s : String) : Type := toType s.toList
  where
    toType : List Char -> Type
      | [] => Unit
      | '%' :: 'd' :: rest => Int × toType rest
      | '%' :: 's' :: rest => String × toType rest
      | _ :: rest => toType rest


@[reducible] -- `abbrev` is just a shorthand for `reducible` attribute
def ArgTypes' : List Char  Type
  | [] => Unit
  | '%' :: 'd' :: rest => Int × ArgTypes' rest
  | '%' :: 's' :: rest => String × ArgTypes' rest
  | _ :: rest => ArgTypes' rest

abbrev ArgTypes'' (s : String) : Type := ArgTypes' s.data


example : ArgTypes "abc" = Unit := rfl
example : ArgTypes "%s is %d" = (String × Int × Unit) := rfl
example : ArgTypes "%s is %d - %d" = (String × Int × Int × Unit) := rfl

#check Simplify.apply (ArgTypes "abc") -- fails because ArgTypes and String.toList are not `abbrev`
#check Simplify.apply (ArgTypes' ['a', 'b', 'c'])
#check Simplify.apply (ArgTypes' "abc".toList) -- fails because String.toList is not `abbrev`
#check Simplify.apply (ArgTypes' "abc".data)
#check Simplify.apply (ArgTypes'' "abc")

Tomas Skrivan (Oct 17 2022 at 09:38):

I didn't study your example properly but it looks like that your Simplify class is dealing exactly with the problem that naive implementation of TypePower produces α × ... × α × Unit

abbrev TypePowerNaive (α : Type) : Nat  Type
  | 0 => Unit
  | n+1 => α × TypePowerNaive α n

abbrev TypePowerCorrect (α : Type) : Nat  Type
  | 0 => Unit
  | 1 => α
  | n+2 => α × TypePowerCorrect α (n+1)

example : TypePowerNaive Nat 2 = (Nat × Nat × Unit) := by rfl
example : TypePowerCorrect Nat 2 = (Nat × Nat) := by rfl

Juan Pablo Romero (Oct 17 2022 at 19:00):

@Tomas Skrivan Ahh, I see. the inner function toType needed to be marked as reducible as well. Nice.


Last updated: Dec 20 2023 at 11:08 UTC