Zulip Chat Archive

Stream: new members

Topic: An Arrow instance for Lean functions


corrededCrustacean (Jun 21 2024 at 08:31):

Hello, I'm trying to implement the Arrow typeclass in Lean, and I'm having trouble implementing it for Lean functions. Here's what I have:

class Arrow (Arrow : Type -> Type -> Type) where
  compose {A B C : Type} : Arrow B C -> Arrow A B -> Arrow A C
  first {A B X : Type} : Arrow A B -> Arrow (A×X) (B×X)
  second {A B X : Type} : Arrow A B -> Arrow (X×A) (X×B)

instance : Arrow (· -> ·) where
  compose := fun f g => f  g
  first f := fun (a,x) => (f a, x)
  second g := fun (x,a) => (x, f a)

The following works, but I have to specify the arrow constructor:

#check Arrow.compose (·+1) (·*2) (Arrow := (· -> ·))

I would like to be able to do this:

#check Arrow.compose (·+1) (·*2)

But it gives the following error:

./src/./Circuit.lean:12:27: error: application type mismatch
  Arrow.compose ?m.446 fun x => x * 2
argument
  fun x => x * 2
has type
  Nat → Nat : Type
but is expected to have type
  ?m.391 ?m.393 ?m.394 : Type

I think it's because (· -> ·) is universe-polymorphic so it isn't precisely Type -> Type -> Type:

#check (· -> ·)
-- fun x x_1 => x -> x_1 : Sort u_1 -> Sort u_2 -> Sort (imax u_1 u_2)

And indeed if I make a simpler wrapper it works:

inductive Function : Type -> Type -> Type where
  | Fun {A B : Type} (f : A -> B) : Function A B

-- instance : Arrow Function where ...

#check Arrow.compose (Function.Fun (·+1)) (Function.Fun (·*2)) -- this works

I've tried making a universe-polymorphic Arrow class, but I couldn't find a way to make it work. I've tried this:

class Arrow (Arrow : Sort u_1 -> Sort u_2 -> Sort (imax u_1 u_2)) where
  compose {A : Sort u} {B : Sort v} {C : Sort w} :
    Arrow B C -> Arrow A B -> Arrow A C
  -- ...

Which doesn't work because the u_1 and u_2 are fixed for the Arrow type constructor, so Arrow B C doesn't typecheck since B : Sort v and not B : Sort u_1. I'd need to be able to write something like:

class Arrow (Arrow : {u_1 u_2 : Universe} -> Sort u_1 -> Sort u_2 -> Sort (imax u_1 u_2)
  -- ...

But I couldn't find how to do that in Lean.

Chris Bailey (Jun 21 2024 at 10:34):

I'm very tired, but I think this is just a typeclass synthesis issue you can fix either by making the type parameter an outParam, or making the instance a default instance.

class Arrow (Arrow : outParam (Type -> Type -> Type)) where
...

or

@[default_instance]
instance : Arrow (· -> ·) where
  compose := fun f g => f  g
  first f := fun (a,x) => (f a, x)
  second g := fun (x,a) => (x, g a)

You can read about these features here and here.

I implore you to have mercy on your users/readers and consider not giving the type parameter the same name as the class.

Chris Bailey (Jun 21 2024 at 10:35):

Also I think if you use this in context, where the type can be inferred, you might not need either.

corrededCrustacean (Jun 21 2024 at 10:54):

Thank you, indeed both methods you proposed work!


Last updated: May 02 2025 at 03:31 UTC