-
Notifications
You must be signed in to change notification settings - Fork 23
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Props & quantifiers #217
Props & quantifiers #217
Conversation
`holds` tries its best to convert `Prop` to `Bool`. Once we add things like `forall` and `exists`, it may not terminate. For now all it does it just evaluate the underlying `Bool` value.
So far they parse, typecheck, and pretty-print. No evaluation yet.
Move the definition of SortMap from Disco.Types.Rules into Disco.Types, in preparation for adding SortMap to PolyType (allowing qualified types to show up in surface syntax). See #179.
There could possibly be even more -- the (There's also a tiny bit of code right now in
Ah, oops, yeah. One quantifier with several bindings is desugaring differently from nested (matching) quantifiers, and the current attempt only works when there aren't intervening cases. I guess there are a couple levels of sophistication we could go for here:
(Illustrative but not super realistic example:)
|
Interesting, I'd have to think about this more. I don't like the sound of "weird dependency between the typechecker and the representation of terms in the evaluator" but I'm not sure I completely understand all the issues. Let's definitely not worry about it for now and maybe we can clean things up even more down the road if we find a reason to.
Oh, I wondered about that bit of code! When I saw it I thought, "wait, doesn't
Ugh, I don't like this option at all. I'd really like to be able to tell students that
Yeah, now that I think about it, I think the desugarer is the right place. We already should change the desugarer so it avoids inserting redundant cases. We might as well put in something to collect repeated quantifiers. It is a bit odd though, it feels sort of like "sugaring" rather than "desugaring". Option 3 is really interesting, and the example is compelling (if not inherently realistic). I'm not sure it's worth doing right now (unless it turns out to be really easy?), but we should keep it in mind. |
The Travis build was failing due to a Haddock parse error I created earlier. Hopefully should be fixed now. |
I agree it's a little odd. If we were doing it just to lambdas it would feel more like a core-to-core optimization pass -- increasing the arity of a lambda to avoid immediately returning a closure, which incidentally makes it less strict. But doing it to quantifiers is less like an optimization with a minor semantic side effect and more like a semantic transformation with a minor performance benefit. I thought a little more about option 3 and it seems like doing it in generality would require fair backtracking search. That sounds like a pain... except that (That said, despite my excitement about this, it's probably better to just do (2) for now.) |
Yeah, the |
|
src/Disco/Desugar.hs
Outdated
-- Type helpers | ||
------------------------------------------------------------ | ||
|
||
mkFunTy :: [Type] -> Type -> Type |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm pretty sure this function already exists somewhere in the Disco codebase...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In fact the place it exists (local to typechecking abstractions) is passing the answer right along, and in the place I though I needed to do this it had already been done. Maybe it belongs in Types
but for now I just took it out and un-abstracted it back into desugarDefn
.
How hard would it be to add another quantifier,
|
I don't think that would be too hard. It's a new abstraction with its own typing rule, but on the evaluation end we're actually already generating the witness and then throwing it away. |
@shaylew , let me know if there's any way I can help on this, or let me know if there are specific parts you're currently working on so I can pick a different aspect to work on. For example, I could start working on getting rid of the special |
I made a first pass at unifying propositions and properties. This pass is just the evaluator and not the frontend, so there's still a separate path for parsing and typechecking properties to be simplified away. I think the major fiddly bit left is dealing with quantified argument names. Currently they get lost and replaced with deeply unhelpful ( Maybe the thing to do here is to capture the environment at the point of failure, then somehow prune it to the variables of interest? Possibly sticking some more information on The other (less fiddly) things to do are to recover "these things weren't equal" reporting with a Prop-valued equality operator, and to parse and typecheck properties as plain old expressions (of type Prop). There are also definitely some opportunities for cleanup (e.g. finding a monoid for the expanded |
@byorgey Do you have opinions about how to unify the syntax of properties and propositions? Right now properties don't require parenthesis around parameters but do require commas between them; but forall/exists in expressions work the other way around (like lambdas).
The former style is definitely closer to math notation, the latter is consistent with lambdas. There's no issue with supporting one or the other or both, it's just down to what syntax(es) you want to support on which abstractions. |
Ah, good question, I had forgotten about this. I definitely prefer the version with commas and no parentheses, for the reason you mention --- it is more in line with the usual mathematical syntax. I think while we're at it we should just change the syntax of lambdas to match, i.e. |
This is all looking good, by the way. The issue with names for reporting counterexamples is tricky, I'd be happy to chat about that sometime if you want. |
So
|
-- (look for a counterexample, fail if you find it) and the motive | ||
-- @(True, True)@ corresponds to "exists". The other values | ||
-- arise from negations. | ||
newtype SearchMotive = SearchMotive (Bool, Bool) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we actually making use of both booleans, or is this left over from the stuff with a prim to invert a prop? I tried tracing its use and it took me to invertMotive
, which took me to primNotProp
, which took me to ONotProp
, which does not seem to be used at all.
If we are in fact not making use of this extra flexibility, should we get rid of it, or should we leave it in just in case we want it in the future?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're only using two values right now (exists is "succeed if you find a success" and forall is "fail if you find a failure"). The search code is slightly simplified by being able to abstract over "what am I looking for" and "what do I do when I find it", but without the NotProp stuff (which gives rise to the other two values) it'd make just as much sense to do a case and determine those locally.
Up to you if you think you're going to want negation, I think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to clarify, when you say "if you're going to want negation", you mean a Prop
-level negation, right? I guess it would be nice to have if most of the machinery is already there. And actually we kind of do already have it, except not:
Disco> :type not (exists x:N. x == x)
not (∃(x : ℕ). x == x) : Prop
Disco> :test not (exists x:N. x == x)
user error (Pattern match failure in do expression at src/Disco/Interpret/Core.hs:372:3-12)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Yeah, I meant prop-level negation.)
Oh huh, I didn't think that would typecheck! But in fact not
, and
, and or
all check at Prop -> Prop
as well as the expected B -> B
and B -> Prop
, unlike identical user-defined boolean functions!
In the case of not
it'd be easy to not desugar it away and make one connective that works for either booleans or props, though I don't know if that's clearer or less clear than using a separate name for the prop version. But this works for not
only because we can push it inside searches in the evaluator (by twiddling the motive). There's no representative in our type of ValProp normal forms that could represent the and
or or
of two searches.
edit: Oh no, there is a way to do this at the desugarer level. You can turn p and q
and p or q
for props into:
forall b: B. {? p if b, q otherwise ?}
exists b: B. {? p if b, q otherwise ?}
And this is the desugarer, so it's even possible to do this without generating a reporting frame for the introduced variable. I'm not sure how I feel about this but if it's possible to desugar the connectives differently based on whether they're taking booleans or props this is a workable approach.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But in fact not, and, and or all check at Prop -> Prop as well as the expected B -> B and B -> Prop, unlike identical user-defined boolean functions!
Yes, that's because of this code here: https://github.com/disco-lang/disco/pull/217/files#diff-6f4e13d554f1399d016fcb9dc348a635R477 I made those changes to the type checker intentionally when I started adding Prop
but I hadn't really thought through all the implications yet.
Long-term, I think it would be ideal for Prop
to support and
and or
(and possibly not
), so one can start talking about proofs and evidence etc., maybe even connect Disco
to some sort of external theorem provers and/or proof assistants, etc.
I was in the middle of writing this when I saw your edit. I was going to say that it seems clear at this point we should push it off to the future, put the type checking for and
, or
, not
back the way they were, and create an issue to track this feature for the future. So now I guess it's less clear, but maybe still a good idea. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's still a good idea to put it off. That trick gets the right answers but it's going to take some work to also get good error messages -- you really don't want to see "I tried 2 things and couldn't find an example" when you ask :test false or false
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, makes sense.
src/Disco/Property.hs
Outdated
generateSamples :: SearchType -> E.IEnumeration a -> Disco e [a] | ||
generateSamples Exhaustive e = return $ E.enumerate e | ||
generateSamples (Randomized n m) e | ||
| E.Finite k <- E.card e, k <= n + m = return $ E.enumerate e |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In another comment I asked why when I tested something of the form forall x:B. ...
I was getting a message that said something about trying 100 possibilities. Looking at this code, it seems like what is going on is that under the hood it actually just tried the two boolean values, but the SearchType
value remained the same, and that's what the pretty-printer used to decide what message to display. Maybe generateSamples
should also return a potentially updated SearchType
value??
This is so cool! I think it's looking really good. After fixing the various minor things I mentioned in comments above, we should also (probably in this order):
|
Use props as properties; unify syntax; add =!=. This touches a large amount of disco code because it changes the syntax of all multi-argument `lambda`, `forall`, and `exists` expressions to match the comma-separated syntax of test properties.
This is looking good. What's left to do before merging? |
Restyle Props & quantifiers
(Previous discussion at #200.)