Search code examples
dependent-typeidris

Understanding 'impossible'


Type-Driven Development with Idris presents:

twoPlusTwoNotFive : 2 + 2 = 5 -> Void
twoPlusTwoNotFive Refl impossible

Is the above a function or value? If it's the former, then why is there no variable arguments, e.g.

add1 : Int -> Int 
add1 x = x + 1

In particular, I'm confused at the lack of = in twoPlusTwoNotFive.


Solution

  • impossible calls out combinations of arguments which are, well, impossible. Idris absolves you of the responsibility to provide a right-hand side when a case is impossible.

    In this instance, we're writing a function of type (2 + 2 = 5) -> Void. Void is a type with no values, so if we succeed in implementing such a function we should expect that all of its cases will turn out to be impossible. Now, = has only one constructor (Refl : x = x), and it can't be used here because it requires ='s arguments to be definitionally equal - they have to be the same x. So, naturally, it's impossible. There's no way anyone could successfully call this function at runtime, and we're saved from having to prove something that isn't true, which would have been quite a big ask.

    Here's another example: you can't index into an empty vector. Scrutinising the Vect and finding it to be [] tells us that n ~ Z; since Fin n is the type of natural numbers less than n there's no value a caller could use to fill in the second argument.

    at : Vect n a -> Fin n -> a
    at [] FZ impossible
    at [] (FS i) impossible
    at (x::xs) FZ = x
    at (x::xs) (FS i) = at xs i
    

    Much of the time you're allowed to omit impossible cases altogether.

    I slightly prefer Agda's notation for the same concept, which uses the symbol () to explicitly pinpoint which bit of the input expression is impossible.

    twoPlusTwoNotFive : (2 + 2 ≡ 5) -> ⊥
    twoPlusTwoNotFive ()  -- again, no RHS
    
    at : forall {n}{A : Set} -> Vec A n -> Fin n -> A
    at [] ()
    at (x ∷ xs) zero = x
    at (x ∷ xs) (suc i) = at xs i
    

    I like it because sometimes you only learn that a case is impossible after doing some further pattern matching on the arguments; when the impossible thing is buried several layers down it's nice to have a visual aid to help you spot where it was.