Zulip Chat Archive
Stream: general
Topic: Parent to Subtype coercion
nrs (Oct 24 2024 at 20:53):
def Digit := @Subtype Char (fun c => c.isDigit)
Is it possible to define a coercion such that the following will typecheck?
def myDigit : Digit := '5'
Kyle Miller (Oct 24 2024 at 20:56):
Option 1: define a Coe instance, and if the data is invalid, replace it with a default value. Maybe use panic! too so you get a runtime error that this happened.
Option 2: define a term elaborator toDigit and write toDigit '5', where the elaborator synthesizes a proof that Char.isDigit holds.
Kyle Miller (Oct 24 2024 at 20:56):
Any particular reason you didn't write it in the following more canonical way?
def Digit := { c : Char // c.isDigit }
nrs (Oct 24 2024 at 20:58):
Kyle Miller said:
Option 1: define a Coe instance, and if the data is invalid, replace it with a default value. Maybe use
panic!too so you get a runtime error that this happened.Option 2: define a term elaborator
toDigitand writetoDigit '5', where the elaborator synthesizes a proof thatChar.isDigitholds.
I've been attempting the Coe instance path, but how do I suspend evaluation of the proof until the actual coercion? e.g. the following will fail because the proof evaluates immediately
instance : Coe Char Digit where
coe c := .mk c (fun c => c.isDigit)
Kyle Miller (Oct 24 2024 at 20:59):
That fails also because (fun c => c.isDigit) is not a proof of c.isDigit
nrs (Oct 24 2024 at 20:59):
Kyle Miller said:
def Digit := { c : Char // c.isDigit }
ah this is neat! ty
Kyle Miller (Oct 24 2024 at 20:59):
but in any case, you have to think about the fact that a coercion is a function Char -> Digit. It has to succeed on all Chars
Kyle Miller (Oct 24 2024 at 21:00):
(I figured you might have known about this syntax given your question yesterday.)
nrs (Oct 24 2024 at 21:02):
Kyle Miller said:
but in any case, you have to think about the fact that a coercion is a function
Char -> Digit. It has to succeed on allChars
so necessarily even 'a' and 'b' will need to coerce to a Digit?
Kyle Miller (Oct 24 2024 at 21:02):
Yes, that's the contract for Coe
Kyle Miller (Oct 24 2024 at 21:02):
Here's an example:
def Digit := { c : Char // c.isDigit }
instance : Inhabited Digit := ⟨'0', rfl⟩
def Digit.ofChar (c : Char) : Digit :=
if h : c.isDigit then
⟨c, h⟩
else
panic! "not a digit"
instance : Coe Char Digit where
coe := Digit.ofChar
def myDigit : Digit := '5'
#eval myDigit
-- '5'
nrs (Oct 24 2024 at 21:03):
Kyle Miller said:
Here's an example:
def Digit := { c : Char // c.isDigit } instance : Inhabited Digit := ⟨'0', rfl⟩ def Digit.ofChar (c : Char) : Digit := if h : c.isDigit then ⟨c, h⟩ else panic! "not a digit" instance : Coe Char Digit where coe := Digit.ofChar def myDigit : Digit := '5' #eval myDigit -- '5'
ah I see! thank you very much for the example!
Kyle Miller (Oct 24 2024 at 21:03):
Then
def myBadDigit : Digit := 'A'
#eval myBadDigit
/-
PANIC at Digit.ofChar lean.run.foo:65:4: not a digit
'0'
-/
Kyle Miller (Oct 24 2024 at 21:04):
Or you can use CoeDep for specific characters:
instance : CoeDep Char '0' Digit where
coe := Digit.ofChar '0'
instance : CoeDep Char '1' Digit where
coe := Digit.ofChar '1'
-- ...
instance : CoeDep Char '5' Digit where
coe := Digit.ofChar '5'
-- ...
def myDigit : Digit := '5'
#eval myDigit
-- '5'
nrs (Oct 24 2024 at 21:05):
Kyle Miller said:
Or you can use
CoeDepfor specific characters:instance : CoeDep Char '0' Digit where coe := Digit.ofChar '0' instance : CoeDep Char '1' Digit where coe := Digit.ofChar '1' -- ... instance : CoeDep Char '5' Digit where coe := Digit.ofChar '5' -- ... def myDigit : Digit := '5' #eval myDigit -- '5'
oh this is a neat solution! maybe there is a way to make a recursive definition here that will work?
Kyle Miller (Oct 24 2024 at 21:06):
No this is very limited, it looks for (more-or-less) the specific syntax. The '5' here must be a literal.
Kyle Miller (Oct 24 2024 at 21:07):
Here's the 'metaprogramming' approach.
def Digit := { c : Char // c.isDigit }
notation "toDigit " c => (Subtype.mk c rfl : Digit)
def myDigit : Digit := toDigit '5'
#eval myDigit
-- '5'
Kyle Miller (Oct 24 2024 at 21:08):
This only works if rfl is a proof of c.isDigit = true (recall: c.isDigit is a Bool, so when it appears where a Prop is expected it is coerced to c.isDigit = true)
nrs (Oct 24 2024 at 21:09):
Kyle Miller said:
This only works if
rflis a proof ofc.isDigit = true(recall:c.isDigitis aBool, so when it appears where aPropis expected it is coerced toc.isDigit = true)
I see, thank you very much for the examples, these are very useful!
Last updated: May 02 2025 at 03:31 UTC