The above code does not typecheck with Liquid Haskell, throwing a liquid type mismatch on the e
in the definition of unwrap
:
Liquid Type Mismatch
.
The inferred type
VV : {v : () | x_ == y_
&& v == e}
.
is not a subtype of the required type
VV : {VV : () | x == y}
.
in the context
x_ : a
y_ : a
e : {e : () | x_ == y_}
x : a
y : a
|
32 | unwrap x y ew@(EqWrapped x_ y_ e) = e
| ^
The problem here is that the type ew :: EqW a {x} {y}
requires that x = x_
and y = y_
. However, LH doesn't aknowledge this, and assumes x_
and y_
are totally independent. This yields the error where the inferred type of e
contains the refinement x_ = y_
, but this isn't enough to convince LH that x = y
which is the desired refinement.
In dependently typed languages like Agda and Coq (some degree of dependent types are required for this situation to arise), you can indicate that a name binding is redundant due to forced equality enforced by types by using an _
(or in the case of agda, a .
prefix aka the dot pattern).
It turns out that this is not actually an issue with LH. If the above code did typecheck, it would allow, for example, the following unsound case.
You could define
and then call
with a
EqWrap
object that was built using a differentx
andy
.In the definition of
ew1'
, which isew1 withProof ew0
, the term thatew1'
is equal to isew1
, but the predicate that providesEqW Int 0 0
comes fromew0
. So if you unwrapew1'
, you don’t get anEqWrapped
corresponding to 1 = 1 like you might expect.This example was provided by @rjhala.