Skip to content

Smart constructors #137

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

Merged

Conversation

phillipharr1s
Copy link
Contributor

No description provided.

alphaRename = (\case
Forall_ s1 v p -> go Forall_ s1 v p
Exists_ s1 v p -> go Exists_ s1 v p
_ -> error "Input must be an Exist or Forall at the top level"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make this function total? For example, if its argument is not a binder, could it just return the argument unmodified? Or, can we encode this restriction in the type?

@@ -0,0 +1,124 @@
{-|
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it necessary to implement substitution ourselves, or could we outsource this to a library like bound?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. Maybe. There was already a substitution function in Kore/Substitution, but I needed one that substitutes terms for terms and not just terms for variables.

-> CommonPurePattern level
-> CommonPurePattern level
-> CommonPurePattern level
subst a b (Forall_ s1 v p) = handleBinder a b Forall_ s1 v p
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be better to write this as subst a b = \case ... since the first two variables are always bound the same.

-> CommonPurePattern level
subst a b (Forall_ s1 v p) = handleBinder a b Forall_ s1 v p
subst a b (Exists_ s1 v p) = handleBinder a b Exists_ s1 v p
subst a b pat =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The intention here might be clearer using guards rather than if.

subst a b (Forall_ s1 v p) = handleBinder a b Forall_ s1 v p
subst a b (Exists_ s1 v p) = handleBinder a b Exists_ s1 v p
subst a b pat =
if pat == a then b else Fix $ fmap (subst a b) $ unFix pat
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think subst could actually be rewritten as a catamorphism using embed. Maybe that's a little tricky if we're doing whole-term substitution instead of only variables. Although, maybe not: I don't see that pat could contain a as a subterm and also be equal to a.

Copy link
Contributor

@virgil-serbanuta virgil-serbanuta left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the test coverage for the new code?

Rewrites_ s2 a b -> Rewrites_ <$> o s2 <*> c a <*> c b
Top_ s2 -> Top_ <$> o s2
Var_ v -> Var_ <$> var v
other -> error $ "Unknown constructor " ++ show other
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does one still get warnings about not matching all possible cases when using pattern synonyms? If yes, then it may be better to drop the "other" case, because we use --pedantic on Jenkins, which will transform that into an error.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pattern Synonyms + GADTS = GHC definitely can't tell if it's complete or not, so without the "other" it doesn't compile.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, wow, I was waiting to use these synonyms in my condition-evaluation pull request, but now I'm not so sure anymore.

One of the reasons we chose Haskell is because all of the built-in safety, but it seems to me that Haskell programmers don't usually want it that much.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PatternSynonyms are a big problem for completeness checking. For our case, we really only want to be using the synonyms for construction, not matching. The benefit of using Fix is that we should be able to write algebras that only need to match on the underlying functior!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, in this case the COMPLETE pragma works. So writing a complete list of cases with

Fix ( ApplicationPattern (Application ...
Fix (AndPattern (And ...

is equivalent to

App_ ...
And_ ...

and the compiler will know it's complete.

Var_ v -> Var_ <$> var v
other -> error $ "Unknown constructor " ++ show other

inputSort f = patternLens f pure pure pure
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add function signatures? Also, could you document all exported functions?

pattern Or_ s2 a b = Fix (OrPattern (Or s2 a b))
pattern Rewrites_ s2 a b = Fix (RewritesPattern (Rewrites s2 a b))
pattern Top_ s2 = Fix (TopPattern (Top s2))
pattern Var_ v = Fix (VariablePattern v)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why didn't you add patterns for strings and chars?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added

@ehildenb
Copy link
Member

@u-- can we use better commit messages? Try to make them useful for code reviewers. Someone somewhere said "given the commit message, the reviewer should be able to re-implement your commit".

Also, I recommend squashing fixup commits into the commit which introduced the feature. The story "I introduced feature A and then " is a lot simpler than "I introduced feature A and then and then a fixup A.1".

@phillipharr1s
Copy link
Contributor Author

@ehildenb what do you mean w/ squashing? Like branch off the original commit that introduced the feature?

@phillipharr1s
Copy link
Contributor Author

Also, I didn't mean for the Hashable instances in Common.hs to be in this branch... but since it relates to the AST I guess it fits.

Copy link
Contributor

@virgil-serbanuta virgil-serbanuta left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't know I was a reviewer! Most of my comments are about understanding what you did and about having a style that's consistent with the existing code.

-- -> CommonPurePattern Meta

-- No way to make multiline pragma?
{-# COMPLETE And_, App_, Bottom_, Ceil_, DV_, Equals_, Exists_, Floor_, Forall_, Iff_, Implies_, In_, Next_, Not_, Or_, Rewrites_, Top_, Var_, StringLiteral_, CharLiteral_ #-}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this make sense: could you add a comment near Pattern in Common.hs that one should update this list when updating the Pattern case list?

_ -> False


-- Flexible patterns are those which can be
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some of these comments do not have a "|". Does haddock produce documentation from them?

import Data.Kore.ASTUtils.SmartConstructors


subst
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Could you document exported methods?
  • One-letter variable names don't usually help people reading your code. Sometimes that does not matter much, but, as an example, here, the meaning of "a" and "b" can be sort of inferred only when reaching the line | pat == a -> b .

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what to call the "phi_1" and "phi_2" in something like phi[phi_1/phi_2]

Substituted term? Substituting term?

It's made worse by the fact that some papers write it the opposite way, phi[phi_2/phi_1]

I guess I can call them "old" and "new"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Old and new seems fine. Substituted and subtituting is also fine. Wrapping them in data types so that people calling your method do not mix the arguments is even better, but I can't usually convince others that it's worth doing. Anyway, the issue was that you had three patterns as arguments and it was not obvious which is which.

go binder s1 v p =
binder s1 (replacementVar v p)
(subst (Var_ v) (Var_ $ replacementVar v p) p)
replacementVar v p = head $ alternatives v \\ (S.toList $ freeVars p)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just curious, you don't have to fix this: why not filter with ('member' freeVars p)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The thing about code like filter (member freeVars p) is, if you later change to a situation where asymptotics matter, it's easy to make a linear operation accidentally quadratic that way.

For instance if alternatives were also a Data.Set, set union/intersect/subtraction are O(m+n), but filtering on membership would be O(m log n).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't it O(m*n) right now, where m is the first index that was not already used, and n is the free var count?

Even if my estimate is correct, I guess it may be reasonable if m is likely to be small (which, by default, it should be). I can't tell right now if it would increase when doing many similar substitutions as we execute code and produce proofs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoops, I thought \ assumes sorted. Yeah, it is slower in this case.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But I just noticed: freeVars p is being called over and over in the "filter" case. That's another accident that can't happen when you use operations over whole datastructures like union/intersect/difference.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. As I said, I was just curious. We're not supposed to optimize things right now, and we should prefer code that is safer/easier to read/whatever.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I'm just explaining why I think it's a good default. For now I filtered with S.member, in the future maybe substitution could be outsourced to kmett's bound or something.


substitutions :: TestTree
substitutions = testGroup "substitutions"
[ testCase "subTrivial" $ assertEqual "" subTrivial subTrivialSolution
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This project uses 4 spaces for indentation almost everywhere, "where" and "=" (only sometimes and only when used in function definitions) are the only exceptions I can think of right now. The main reasons for using 4 spaces are uniformity and the fact that "where" and "=" use half of a normal indentation and it looks bad when they are indented by only one space. Note that function signatures are also indented by 4 spaces instead of 2.

BTW, I think that there are places above where you're using a one-space indentation.

@@ -0,0 +1,129 @@
{-|
Module : Data.Kore.ASTUtils.Substitution
Description : Substitute phi_1 for phi_2, avoiding capture
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a short comment or example saying why this would be useful (as opposed to substituting variables)?

@@ -101,14 +109,16 @@ data Id level = Id
{ getId :: !String
, idLocation :: !AstLocation
}
deriving Show
deriving (Show, Generic)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just curious, not asking for a fix: Where is this generic thing used (for any of the instances defined here, not necessarily this one)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generic is needed to derive classes like Hashable.

@@ -0,0 +1,217 @@
{-# LANGUAGE MultiParamTypeClasses #-}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't get an answer to one of my previous questions, so I'll try asking it this way: What's the test coverage for the new code?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean hpc report ? About 70-80%

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We usually use stack test --coverage and look at the html report, I guess that may use hpc behind the scenes.

That number, by itself, does not mean much. We are aiming for 100% coverage. Until now we allowed the coverage to be lower for things like automatic instance derivations and struct getters, but we usually want close to 100% for everything else. So, if the missing 20-30% are things like that, it should be fine.

Copy link
Contributor

@virgil-serbanuta virgil-serbanuta left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please take a look at the new comments before submitting ans see if they make sense.


-- subst2 a b = cata (\pat -> if Fix pat == a then b else Fix pat)

-- handleBinder
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can leave it if it makes sense, but I'm not a fan of code that's commented out. If you allow time to pass, the code will be more and more out of date, but people will avoid deleting it since they assume it has a purpose.

@@ -0,0 +1,217 @@
{-# LANGUAGE MultiParamTypeClasses #-}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We usually use stack test --coverage and look at the html report, I guess that may use hpc behind the scenes.

That number, by itself, does not mean much. We are aiming for 100% coverage. Until now we allowed the coverage to be lower for things like automatic instance derivations and struct getters, but we usually want close to 100% for everything else. So, if the missing 20-30% are things like that, it should be fine.

@phillipharr1s phillipharr1s merged commit 55f89f0 into runtimeverification:master Jul 12, 2018
@phillipharr1s phillipharr1s deleted the smart-constructors branch July 13, 2018 21:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants