Allow refinements in the output type of measures
gridaphobe opened this issue · 12 comments
Only base types are allowed to appear in the type of a measure, but this is less accurate than it could be, e.g. when dealing with measures that denote the size of a value.
measure len :: [a] -> Int
len [] = 0
len (x:xs) = 1 + len xs
Since the type only specifies that len
returns an Int
, we need to additionally define an invariant on Int
.
invariant {v:[a] | (len v) >= 0}
This is a bit tedious when what we really want to write is just
measure len :: [a] -> Nat
len [] = 0
len (x:xs) = 1 + len xs
Perhaps there's a good reason to disallow refinements in the return type of a measure, but it isn't apparent to me.
I believe that the invariant is basically needed to get the qualifiers,
with the correct qualifiers liquidHaskell CAN infer that len v >=0
by len
is definition.
No it is not just for qualifiers.
The non negativity of Len can only be inferred for lists that are constructed from scratch.
Try to prove that haskell's 'drop' function returns a list with nonneg Len.
Now you will see you need that the INPUT also has non neg Len. It is to avoid writing such tedious things in the input that we need invariants.
At any rate @gridaphobe is right. It would be nice to put the fact right in the measure. Mind you we will still need the. Invariant mechanism for other things...
On Sep 25, 2013, at 8:15 AM, Niki Vazou notifications@github.com wrote:
I believe that the invariant is basically needed to get the qualifiers,
with the correct qualifiers liquidHaskell CAN infer that len v >=0 by lenis definition.—
Reply to this email directly or view it on GitHub.
Btw, given @nikivazou's recent progress with promoting functions to measures, it would be nice to fix the above, thereby unifying measures and invariants.
So we should be able to write:
{-@ measure len @-}
{-@ len :: [a] -> Nat @-}
len :: [a] -> Int
len [] = 0
len (x:xs) = 1 + len xs
and from that, automatically derive
invariant {v: [a] | 0 <= len v}
How? Well, suppose you have a measure from some type T
to a base type B
with refinement p
meas :: T -> {v:B | p}
Then you have effectively specified (and verified)
invariant {v:T | p[(meas v) / v]}
Yes, this sounds like a nice and simple approach.
I suppose in this approach, the measure-promotion would automatically refine the type of len-the-haskell-function to refer to len-the-measure? Or is @nikivazou already doing that?
Another question, should we allow promotion of functions with pre-conditions?
@ranjitjhala, this is a nice (and sound) suggestion. If we support this we can stop supporting invariants (that can lead to unsoundness).
@gridaphobe we should allows promotion of functions with preconditions, but we should translate the preconditions into the types of the data constructors. (I already relate the len-the-haskell-function with len-the-measure.)
Another suggestion: since we are already becoming a "lightweight" dependent type language, how about a flag "--autopromote" that automatically promotes to logic all the appropriate functions?
Hey, is this still open? I thought @nikivazou implemented it?
It is implemented for Haskell defined measures!
@nikivazou what about for old-style measures? Not all measures can be defined in Haskell, e.g. uninterpreted measures.
Curently, there is no support for invariants over old style measures.
Agree, but abstract measure is just an uninterpreted function. Would need to clarify all these definitions.