We can use a with expression to obtain an implementation that I think is a bit more elegant than using So, which I try to reserve only as a last resort.
||| Test if a Char is an open or close paren.
isParen : Char -> Bool
isParen '(' = True
isParen ')' = True
isParen _ = False
||| Obtain the integer code corresponding to a paren.
||| Proof is required that c is '(' or ')'.
parenCode :
(c : Char) ->
{auto 0 prf_isParen : isParen c === True}
Integer
parenCode c with (isParen c)
parenCode '(' | True = 1
parenCode ')' | True = -1
parenCode _ | False impossible
If we perform the comparison inside the with, the compiler is able to recognize isParen c as identical to the expression as in our prf_isParen.
This generates Scheme code that you would hope for:
(define Misc-isParen
(lambda (arg-0)
(cond ((equal? arg-0 #\() 1)
((equal? arg-0 #\)) 1)
(else 0))))
(define Misc-with--parenCode-8826
(lambda (arg-0 arg-1 arg-2)
(cond
((equal? arg-0 #\() (cond (else 1)))
(else (cond (else -1))))))
(define Misc-parenCode
(lambda (arg-0)
(Misc-with--parenCode-8826 arg-0
(Misc-isParen arg-0)
'erased)))
Although the with generates an extra function call, it only invokes one character comparison.
Also, the Chez compiler is smart enough to optimize away these nested functions:
(expand/optimize
'(let ((Misc-isParen
(lambda (arg-0)
(cond ((equal? arg-0 #\() 1)
((equal? arg-0 #\)) 1)
(else 0))))
(Misc-with--parenCode-8826
(lambda (arg-0 arg-1 arg-2)
(cond ((equal? arg-0 #\() (cond (else 1)))
(else (cond (else -1))))))
(Misc-parenCode
(lambda (arg-0)
(Misc-with--parenCode-8826 arg-0
(Misc-isParen arg-0)
'erased))))
(Misc-parenCode #\()))
(Misc-with--parenCode-8826 #\( (Misc-isParen #\() 'erased)
You can also use with in this manner to construct the required proof that isParen c === True, by using the extra proof binding:
parenCodeTry : (c : Char) -> Maybe Integer
parenCodeTry c with (isParen c) proof prf_isParen
_ | False = Nothing
_ | True = Just $ parenCode c
In the True branch of parenCodeTry, the type of prf_isParen will be:
prf_isParen : Equal (isParen c) True
which will allow the compiler to find a value for the auto 0 prf_isParen parameter.
In the False branch of parenCodeTry, it will be:
prf_isParen : isParen c = False
which of course is not a valid value for the auto 0 prf_isParen parameter, and therefore you would not be able to invoke parenCode in that branch.