1

I have the following code:

doSomething : (s : String) -> (not (s == "") = True) -> String
doSomething s = ?doSomething

validate : String -> String
validate s = case (not (s == ""))  of
                  False  => s
                  True => doSomething s 

After checking the input is not empty I would like to pass it to a function which accepts only validated input (not empty Strings).

As far as I understand the validation is taking place during runtime but the types are calculated during compile time - thats way it doesn't work. Is there any workaround?

Also while playing with the code I noticed:

:t (("la" == "") == True)
"la" == "" == True : Bool

But

:t (("la" == "") = True)
"la" == "" = True : Type

Why the types are different?

Cactus
  • 27,075
  • 9
  • 69
  • 149
Blezz
  • 313
  • 1
  • 10

2 Answers2

2

This isn't about runtime vs. compile-time, since you are writing two branches in validate that take care, statically, of both the empty and the non-empty input cases; at runtime you merely choose between the two.

Your problem is Boolean blindness: if you have a value of type Bool, it is just that, a single bit that could have gone either way. This is what == gives you.

= on the other hand is for propositional equality: the only constructor of the type(-as-proposition) a = b is Refl : a = a, so by pattern-matching on a value of type a = b, you learn that a and b are truly equal.

I was able to get your example working by passing the non-equality as a proposition to doSomething:

doSomething : (s : String) -> Not (s = "") -> String
doSomething "" wtf = void $ wtf Refl
doSomething s nonEmpty = ?doSomething

validate : String -> String
validate "" = ""
validate s = doSomething s nonEmpty
  where
    nonEmpty : Not (s = "")
    nonEmpty Refl impossible
Cactus
  • 27,075
  • 9
  • 69
  • 149
  • 3
    There are two points about "boolean blindness": booleans allow you to cheat equality and "prove" that two things are equal, even if they are not, and booleans lose information. The first claim is silly: setoids allow you to cheat equality as well, but everybody is happy with them. And there is nothing wrong about information forgetting in general. In a dependently typed language you can always recover it. Sometimes it's just needed to erase some type-level stuff (like with heterogeneous equality or lists instead of vectors). It's not "boolean blindness", it's "boolean eyes closing". – effectfully Sep 14 '15 at 23:24
1

As far as I understand the validation is taking place during runtime but the types are calculated during compile time - thats way it doesn't work.

That's not correct. It doesn't work because

  • We need the with form to perform dependent pattern matching, i. e. perform substitution and refinement on the context based on information gained from specific data constructors.

  • Even if we use with here, not (s == "") isn't anywhere in the context when we do the pattern match, therefore there's nothing to rewrite (in the context), and we can't demonstrate the not (s == "") = True equality later when we'd like to call doSomething.

We can use a wrapper data type here that lets us save a proof that a specific pattern equals the original expression we matched on:

doSomething : (s : String) -> (not (s == "") = True) -> String
doSomething s = ?doSomething

data Inspect : a -> Type where
  Match : {A : Type} -> {x : A} -> (y : A) -> x = y -> Inspect x

inspect : {A : Type} -> (x : A) -> Inspect x
inspect x = Match x Refl

validate : String -> String
validate s with (inspect (not (s == "")))
  | Match True  p = doSomething s p
  | Match False p = s
András Kovács
  • 29,931
  • 3
  • 53
  • 99
  • Can you explain what you mean about not having `not (s=="")` in the context when you do the pattern matching? – dfeuer Sep 26 '15 at 15:44
  • @dfeuer if we had something with a type mentioning `not (s == "")` on the left side of the equation (in OP's `validate`), dependent pattern matching would have to rewrite that to `True` or `False`. If we don't have type dependencies in the context, then pattern matching doesn't give us extra information. – András Kovács Sep 26 '15 at 17:04