FStar
FStar copied to clipboard
Define our logical connectives in `prop`
We can currently define a prop type in various ways, for instance:
type prop = a:Type0{ forall (x:a). x === () }
or (maybe) better:
abstract let prop (* : Type1 *)
= a:Type0{ l_forall #a (fun x -> l_forall #a (fun y -> l_equals x y)) }
Started working on an experiment to switch our logical connectives to prop this fall on the catalin_prop branch: https://github.com/FStarLang/FStar/blob/catalin_prop/ulib/prims.fst#L127
Quickly run into lots of troubles, including the fact that making prop abstract means that going from prop to Type needs to be coercive (a p2t, something like b2t). There were more issues, but unfortunately I can no longer find my notes on this, so will need to rediscover them when returning to this.
One big advantage of this change would be that we would get much better error messages for types that make no sense logically, and that we could get rid of the logic qualifier hack.
Finally found my old notes on this by Gmail search in commit messages. Hallelujah!
(they were buried in <fstar-priv>/papers/dm4free/popl17/final-version.org)
TODO (Catalin) Switching to prop instead of Type -- punted
CH: We didn't promise this but it would make the presentation much nicer.
Current plan (take 3):
- Stage 1 (final version):
- change
prims.fstto add a 3rd layer of logical operators:- layer 1 (
c_prefix): constructive logic - layer 2 (
l_prefix): squashed operators - layer 3 (
p_prefix): prop operators (new!)- defined as the
l_operators but with stronger prop types - operators like
\/and/\andforallshould desugar to this
- defined as the
- layer 1 (
- change parsing to produce
p_operators instead ofl_ones - change logical encoding to treat
p_andl_ops the same - remove the
logicalkeyword and its syntactic special casing - [Kenji can help] make dm4free turn
M tinto(t -> prop) -> prop - make this work for
examples/dm4freeonly - change the paper to use
propfor the CPS
- change
- Stage 2 (after final version)
- push this change to all std library and examples
- improve the SMT encoding
- to treat
squash (c_ ...)the same asl_andp_ - to automatically remove double squashes
- to treat
- try to reduce the number of squashes
- c_true, c_false, and even c_and/or p1 p2 when p1 and p2 are prop are already in prop even without the extra squash, so we could consider making p_true be c_true directly and removing l_true, etc
- some discussion about
l_not being a good name for Z3- could rename it to
s_maybe (for squash)?
- could rename it to
- Pending issues:
- making
propabstract prevents subtyping betweenpropandType- was planning to make prop abstract and use an escape hatch coercion between a sub-singleton Type and prop ... but we didn't think about the prop to Type direction; explicitly coercing that way could be painful? Although, will that be so frequent once we make all our combinators strongly prop typed? More motivation for the point below then!
- shouldn't the
x:t{p}notation requirepto be aprop?- I think we should, since this would prevent stuff like
x:int{int} - we would still need some way to reference the primitive refinements, but they would only be used for defining squash
- I think we should, since this would prevent stuff like
- should try to make PURE private
- l_forall and quantifier patterns
- understand current cheating
- what's the purpose of guard_free?
- maybe create an explicit Meta_pattern node, and use it for the only pattern in PURE?
- think about
b2tvsb2p- a priori we should be able to do a lot better than b2t, which is super syntactic and flaky
- should the constructive operators keep using
GTot?
- making
discussed a bit with Nik about this a few days back
in the short run we can probably hack something up by making prop abstract in prims and bringing in stuff like squash to prims as well ... and after typechecking prims requiring that refinements and WPs use prop [the immediate problem with this is the need for a coercion between prop and Type]
in the less short run we have been discussing in Paris with @kyoDralliam and @danelahman about making prop primitive in order to simplify and streamline our formalization (in particular getting rid of the separate logical entailment judgment completely, and relying on squashed arrows and inductive types for the logical part)
Worked out some of this in @kyoDralliam's uEMFStar.org (which builds on @mtzguido's ufstar.org). The main change is adding a new -1 universe level and defining prop as Type -1. This allows us to build arrows using prop elements (like squash). We assume that inductives can only be defined at levels >= 0.
The rest is syntactically simple, and it basically only involves assuming a bunch of constants:
squash : Πk. α:Type k -> prop
witness : Πk. α:Type k -> α -> squash α
elim_squash : Πk. α:Type k -> squash α -> φ':prop -> (α -> φ') -> φ'
elim_refine : Πk. α:Type k -> φ:(α->prop) -> x:α{φ x} -> φ':prop -> (φ x -> φ') -> φ'
proof_irrelevance : φ:prop -> w:φ -> w':φ -> eq w w'
excluded_middle : φ:prop -> φ \/ ~φ (where \/ is the squashed sum)
... before being able to define all logical connectives in prop:
let (==) k x y = squash (eq k x y)
let coerce (φ:prop) : Type Z = x:unit{φ}
let (/\) (φ φ' : prop) : prop = squash (pair Z Z (coerce φ) (coerce φ'))
let (\/) (φ φ' : prop) : prop = squash (sum Z Z (coerce φ) (coerce φ'))
let (=>) (φ φ' : prop) : prop = squash (φ -> φ')
let forall k (α:Type k) (f: (α -> prop)) : prop = squash (x:α -> f x)
let true : prop = squash unit
let false : prop = squash empty
let not (φ : prop) : prop = φ => false
It seems simple to turn this into a bunch of axioms and definitions in prims:
assume val squash : Type -> prop
assume val witness : a:Type -> a -> squash a
assume val elim_squash : a:Type -> squash a -> p:prop -> (a -> p) -> p
assume val elim_refine : a:Type -> q:(a->prop) -> x:a{q x} -> p:prop -> (q x -> p) -> p
let l_eq (a:Type) (x y : a) = squash (equals x y)
let coerce (p:prop) : Type0 = x:unit{p}
let l_and (p q:prop) : prop = squash (c_and (coerce p) (coerce q))
let l_or (p q:prop) : prop = squash (c_or (coerce p) (coerce q))
let l_imp (p q:prop) : prop = squash (p -> GTot q)
let l_forall (a:Type) (p:a -> prop) : prop = squash (x:a -> p x)
let l_true : prop = squash c_True
let l_false : prop = squash c_False
let l_not (p:prop) : prop = l_imp p l_false
assume val proof_irrelevance : p:prop -> w:p -> w':p -> equals w w'
assume val excluded_middle : p:prop -> l_or p (l_not p)
Some typing rules also need to change, but it's in the expected way, for instance:
S;G |- t : Type(u)
S;G, x:t |- t' : prop
-------------------------- [T-Refine]
S;G |- x:t{t'} : Type(u)
S;G |- e : t
S;G, x:t |- t' : prop
S;G |= t'[e/x]
-------------------------- [T-RefineI]
S;G |- e : x:t{t'}
The big advantage on the formalization side is that we can completely get rid of the whole S;G |= φ judgment. In return we need a single extra rule about reduction and subtyping:
t ~>* t' S;G |- t : Type(u) S;G |- t' : Type(u)
----------------------------------------------------- [Sub-Red]
S;G |- t <:> t'
We are also looking at a variant that doesn't involve a prop universe, but to me it's less intuitive:
assume val prop : Type0
assume val squash : Type -> prop
let el (p:prop) : Type0 = x:unit{p}
assume val witness : a:Type -> a -> el (squash a)
assume val elim_squash : a:Type -> el (squash a) ->
p:prop -> (a -> el p) -> el p
assume val elim_refine : a:Type -> q:(a->prop) -> x:a{q x} ->
p:prop -> (el (q x) -> el p) -> el p
let l_eq (a:Type) (x y : a) : prop = squash (equals x y)
let l_and (p q:prop) : prop = squash (c_and (el p) (el q))
let l_or (p q:prop) : prop = squash (c_or (el p) (el q))
let l_imp (p q:prop) : prop = squash (el p -> GTot (el q))
let l_forall (a:Type) (p:a -> prop) : prop = squash (x:a -> el (p x))
let l_true : prop = squash c_True
let l_false : prop = squash c_False
let l_not (p:prop) : prop = l_imp p l_false
// we don't need proof irrelevance
assume val excluded_middle : p:prop -> el (l_or p (l_not p))
We also don't know if this would work without a special treatment of the el coercion (aka p2t), both in the implementation and in the theory.
I started implementing a least intrusive solution on the c_prop-dev branch. Here's a list of things still to do:
https://github.com/FStarLang/FStar/blob/c_prop-dev/PropTODO.org
This would also be useful to address issues like #1041.