Reputation: 1575
Say I want to have a Digit
type that is a refinement of Char
to only digits:
open import Data.Bool using (Bool; true; if_then_else_)
open import Data.Char using (Char; isDigit)
open import Data.Maybe using (Maybe; just; nothing)
data IsTrue : Bool → Set where
is-true : IsTrue true
data IsDigit (c : Char) : Set where
is-digit : {p : IsTrue (isDigit c)} → IsDigit c
data Digit : Set where
digit : (c : Char) → {p : IsDigit c} → Digit
I can then construct, e.g., the Digit
for '0'
like so (although the obvious parameters that I seem to need to pass are frustrating):
0-digit : Digit
0-digit = digit '0' {is-digit {_} {is-true}}
However, if I want a function that takes an arbitrary Char
and returns Maybe Digit
, I can't seem to communicate to Agda that in the then
branch, isDigit c
always holds. How do I make this visible to Agda?
In some languages, this would be a form of flow typing, but if I understand correctly, Agda only has "flow typing" when pattern-matching on constructors. It does compile if I exhaustively match on '0', '1', ... '9'
, but that's tedious and seems unfortunate.
maybeFromChar : Char → Maybe Digit
maybeFromChar c =
if isDigit c
then just (digit c {is-digit {c} {is-true}})
else nothing
.../Foo.agda:20,39-46
true != isDigit c of type Bool
when checking that the expression is-true has type
IsTrue (isDigit c)
(Suggestions for improvements to the way I'm modeling Digit
are welcome as well.)
Upvotes: 0
Views: 54
Reputation: 12715
You can use the inspect
idiom to remember the match (you'll need to add false
to the import list of Data.Bool
):
open import Relation.Binary.PropositionalEquality
maybeFromChar : Char → Maybe Digit
maybeFromChar c with isDigit c | inspect isDigit c
... | true | [ eq ] = just (digit c {is-digit {c} {subst IsTrue (sym eq) is-true}})
... | _ | _ = nothing
You can also use the dependent version of if_then_else_
a.k.a. the eliminator of Bool
:
open import Function
open import Data.Maybe as Maybe
elimBool : ∀ {π} → (P : Bool → Set π) → P true → P false → ∀ b → P b
elimBool P t f true = t
elimBool P t f false = f
maybeFromChar' : Char → Maybe Digit
maybeFromChar' c =
Maybe.map (λ p -> digit c {is-digit {_} {p}}) $
elimBool (Maybe ∘ IsTrue) (just is-true) nothing (isDigit c)
Upvotes: 2