Zulip Chat Archive

Stream: Is there code for X?

Topic: explicit class-based conversion


JJ (Aug 28 2024 at 16:48):

lean offers implicit class-based coersion: https://leanprover-community.github.io/mathlib4_docs/Init/Coe.html

does explicit class-based conversion exist? i have found myself writing a considerable amount of helper methods like the below for type conversion and it seems like there simply must be a better way. does something akin to rust's From/Into traits exist?

namespace BitVec
def ofUInt8 (n : Nat) (i : UInt8) : BitVec n :=
  BitVec.ofNat n <| UInt8.toNat i
end BitVec

namespace Array
def ofUInt32 (long : UInt32) : Array Byte :=
  #[UInt8.ofNat <| UInt32.toNat long >>> 24, UInt8.ofNat <| UInt32.toNat long >>> 16,
    UInt8.ofNat <| UInt32.toNat long >>> 8, UInt8.ofNat <| UInt32.toNat long]
end Array

JJ (Aug 28 2024 at 16:49):

i.e. i'm looking for the following:

class Convert (α : Sort u) (β : Sort v) where
  to : α  β

Eric Wieser (Aug 28 2024 at 16:55):

Lean3 had docs3#lift / docs3#has_lift for this

Edward van de Meent (Aug 28 2024 at 17:10):

isn't this just docs#Coe ?

Edward van de Meent (Aug 28 2024 at 17:13):

(see also docs#CoeOut )
i now see you already found that...

JJ (Aug 28 2024 at 17:21):

i'm mostly looking for a way to not need to explicitly say FromType.toNewType foo every time i want to convert types

let a : α := sorry
let b : β := a.to  -- when the type is known, no need to re-specify in method name
let g := a.to[γ]   -- when the type is unknown, specify as a generic

it would also be cool if this would resolve things transitively, i.e. if there is an instance of Convert for α → β and an instance from β → γ, ((a : α).to : γ) would Just Work

Edward van de Meent (Aug 28 2024 at 17:33):

looking at it, i'd say you could do some adapting to use the existing Coe infrastructure... however, i don't know you can get the dot-notation to work...
the following might help:

abbrev coeTo (α :Type u) {β : Type v} [CoeTC β α] : β  α := CoeTC.coe

Edward van de Meent (Aug 28 2024 at 17:39):

which allows coeTo α b assuming [CoeTC β α] exists (rather than the notation of b.coeTo α you suggested)

JJ (Aug 28 2024 at 17:41):

hmm, but my impression is that Coe is for implicit coersion, and implementing Coe or any related classes for some types will add them to that system. i don't want implicit coersion b/c i don't want ex. UInt32 to be implicitly coerced to an Array UInt8, but i would like explicit ((foo : UInt32).to : Array UInt8) to work

Edward van de Meent (Aug 28 2024 at 18:00):

the following might work?

universe u v

class Convert (α :  semiOutParam (Type v)) (β : Type u) where
  convert : α  β

instance Coe.toConvert [Coe α β] : Convert α β where convert := Coe.coe
export Coe (toConvert)

class ConvertTC (α :  semiOutParam (Type v)) (β : Type u) where
  convert : α  β

instance ConvertTC.cons [Convert β γ] [ConvertTC α β] : ConvertTC α γ where
  convert a := Convert.convert (ConvertTC.convert a : β)

instance ConvertTC.single [Convert α β] : ConvertTC α β where
  convert a := Convert.convert a

instance ConvertTC.id: ConvertTC α α where convert a := a

def convertTo (β : Type u) {α : Type v} [ConvertTC α β] : α  β := ConvertTC.convert

this adds the existing Coe classes to the new Convert system, but since these aren't marked with the @[coe] attribute i'd guess that new Convert instances don't get implicitly inserted?

Edward van de Meent (Aug 28 2024 at 18:05):

btw, the TC stands for Transitive Closure, if someone didn't make that connection before

Kevin Buzzard (Aug 28 2024 at 19:16):

I have never understood how the coercion system works in lean 4 with all this CoeTC etc. I wonder if you can just use the coercion system as is but you just have to be an expert to implement it

Edward van de Meent (Aug 28 2024 at 20:52):

Just did a dive into the file defining these coe-classes, and i have to say it is quite enlightening.

the issue with extending the existing infra is that the top class in the hierarchy automatically gets tied into the system: if you add only to classes the bottom, you don't get to add new coercions; if you add only to the top of the hierarchy, everything you do gets tied into the system; if you add a separate hierarchy, you don't get to leverage existing coercions.

there may be a solution using attributes/options/other magic i dont know about. particularly i don't know how how the coe_decl attribute is handled/what it gets used for, which may be significant.
The only solution to this that i can think of would be to add a second layer to the hierarchy which runs parallel on top of the existing one, but is connected per (base) class. This leads to duplicating quite some code, unfortunately...

Eric Wieser (Aug 28 2024 at 22:01):

A way to avoid duplication would be to change Coe X Y : Type to Coe X Y isImplicit

Eric Wieser (Aug 28 2024 at 22:01):

(where isImplicit : Bool or belongs to some enum type)

Eric Wieser (Aug 28 2024 at 22:02):

This would need an RFC, since obviously this is a nontrivial change to core

JJ (Aug 29 2024 at 20:50):

okay, cool. i might make an RFC to extend Coe when i am more familiar with the language. for now, i think i'll play around with implementing my own local Conv class in my projects.

Edward van de Meent (Aug 30 2024 at 07:24):

Eric Wieser said:

A way to avoid duplication would be to change Coe X Y : Type to Coe X Y isImplicit

that avoids duplicating the classes, sure. however, i think that might double/triple the instances necessary to make the transitive synthesis work?

Edward van de Meent (Aug 30 2024 at 07:28):

because you'd need something like this:

instance [Coe β γ NotImplicit] [CoeTC α β x] : CoeTC α γ NotImplicit := ...
instance [Coe β γ x] [CoeTC α β NotImplicit] : CoeTC α γ NotImplicit := ...
instance [Coe β γ IsImplicit] [CoeTC α β IsImplicit] : CoeTC α γ IsImplicit := ...

Edward van de Meent (Aug 30 2024 at 07:42):

at least, i think something like this might not work because of transparency issues?

instance [Coe β γ x] [CoeTC α β y] : CoeTC α γ (x  y) := ...

Edward van de Meent (Aug 30 2024 at 07:50):

or free variable issues?

Edward van de Meent (Aug 30 2024 at 08:02):

having a bit of a try, it requires 4(!) (not factorial) instances due to the free variable issue.


Last updated: May 02 2025 at 03:31 UTC