Case split plugin - #5014
Conversation
|
Everybody is eagerly anticipating this change, thank you! Given the many WIP commits, I just wanted to ask if you could sqaush or reword them. I guess you had this in mind anyways. |
Glad to hear that!
Yep, see 3rd paragraph at the top :P |
|
Ah, the second sub-clause :-P my attention had already shifted before that one, I apologize :-)! Thanks! |
5456d10 to
038348b
Compare
MangoIV
left a comment
There was a problem hiding this comment.
First pass. I think after you clean up the main logic a bit more and add some documentation there, I can take another look. :)
Very good work, looking forward to having this in HLS!
| -> fileDiags | ||
| -- pair each file diag with its ds messages, if any | ||
| & fmap (id &&& getMaybeDsMsg) | ||
| -- discard those with `Nothing` ds messages | ||
| & filter (isJust . snd) | ||
| -- unwrap the surviving `Just`s | ||
| & fmap (second fromJust) | ||
| -- wrap back in the monad | ||
| & pure |
There was a problem hiding this comment.
now you can reinstate mapMaybe again, to get rid of the fromJust.
let extractDiagAndAlts diag dsMsg =
(fdLspDiagnostic diag, dsMsgToPmAlts dsMsg)
pure $
mapMaybe ( \diag -> extractDiagAndAlts diag <$> getMaybeDsMsg diag ) fileDiagsso now you can skip the bimap in the next step.
There was a problem hiding this comment.
I see the advantage this approach causes in "the next step".
But if I have to be honest, I find the last line of your snippet fairly harder to read, because the majority of the line is a lambda, rather than a "vocabulary" word, and I always have to read it all, and reason about it, to remind myself/re-understand what it does.
On the contrary, the mapMaybe sequence takes me less time to understand once I know the in and out types. I mean, if I know the type in is [(a, Maybe b)] and type out is [(a, b)], I know exactly what it takes to go from the former to the latter, and I easily believe that mapMaybe sequence is doing the right job, even without verifying it by hand, because it's just a combination of two existing simple abstractions: the fact that they typecheck reassures me. Unlike the lambda, which contains "hand-written" logic, and so I feel obliged to check that it is doing indeed the right choice.
And even without mapMaybe sequence, if I can rely on individual steps built on top of filter/map/isJust/fromJust, that still makes it more readable.
But I suppose this is just the way I imagine code in front of my eyes vs how you do it?
| pure (old, new) | ||
|
|
||
| where | ||
| go :: forall d m. (MonadState Bool m, MonadReader Bool m, Data d) => d -> m d |
There was a problem hiding this comment.
You can use for instance ExceptT for now. Is it clear why that's better?
And if you want to, you can try
newtype ExceptCT e m a
= MkExceptC {unExceptCT :: forall b. (e -> m b) -> (a -> m b) -> m b} and see if that's faster. But that doesn't have to be done now.
| -- | Version of zipWith3 for `NonEmpty` lists. | ||
| zipWith3' :: (a -> b -> c -> d) -> NE.NonEmpty a -> NE.NonEmpty b -> NE.NonEmpty c -> NE.NonEmpty d | ||
| zipWith3' f as bs cs = NE.fromList | ||
| $ getZipList | ||
| $ f <$> ZipList (NE.toList as) | ||
| <*> ZipList (NE.toList bs) | ||
| <*> ZipList (NE.toList cs) |
There was a problem hiding this comment.
you can avoid having to write zipWith3 and get cleaner code in one go, by applying the review comment that I gave you previously.
There was a problem hiding this comment.
You mean this comment I assume. I had initially done that - before trying to NonEmpty all lists - but at some point I fell back to a 3 way zip. Can't remember why. I'll review that once more.
There was a problem hiding this comment.
tl;dr
In hindsight, I don't think I understood your original suggestion.
How I interpreted your original suggestion
The original code you commented was this:
then zipWith3 (.)
(replicate (length missing - 1) addSemiCol ++ [id])
(repeat $ setDP 1 defaultIndent)and your comment was
couldn't we make this a bit more regular by applying the transformation in the first argument to the second argument before passing it to zipWith ($)?
I didn't quite understand what first and second refer to, so I looked at the target (use zipWith ($)) and thought of a way of rearranging the arguments to do that, and came up with this:
then zipWith ($)
(fmap (repeat (setDP 1 defaultIndent) .) $ replicate (length missing - 1) addSemiCol ++ [id])which is applying the transformation in the third argument to the second argument before passing it to zipWith ($). I thought ok, maybe Magnus was in a rush, counted the arguments of zipWith3 (.) rather then the arguments of zipWith3, and did the typo of swapping "first" and "second".
That interpretation doesn't work on the present code
But this interpretation I gave builds on the fact that the 3rd argument to zipWith3 is a repeat.
I don't quite remember how, but the current code now doesn't have this, so I must assume that I've misunderstood your original suggestion.
Can you clarify?
This PR is for introducing the so called case-split plugin, as requested in #5013.
(In the following, me is myself and we is myself, @fendor, @MangoIV, and @AndreasPK.)
The change as of now needs lots of refinements (obviously beside getting rid of all the
trace*calls I've peppered the code with, and beside squashing all commits together), especially these:maxUncoveredPatternsuncovered patterns, but the plugin would nonetheless insert _all_¹ uncovered patterns;maxUncoveredPatterns, because it's the least invasive approach, it doesn't require any additional code, one can only improve from there, and nothing prevents the user from triggering the plugin more than once to uncover more and more patterns (as noticed by Andreas, this could actually be the gist of some solution to the whole problem, i.e. trigger the plugin repeatedly until no uncovered pattern remains);T16.hs), the indentation of the patterns inserted by the plugins is sometimes too much, although correct,deltaPosand related abstractions work; need to chat with @alanz about this;\casejust as it does forcase(seeT12.hs), but maybe this is as much a generalization as having the plugin work with function definitions, so I guess we can drop this test, or maybe assume it as failing and link a new enhancement request to it?_, but the test doesn't pass (seeT18.hs); I still don't know why;T19.hs? I think it should just not offer any action.caseexpressions, both incomplete, are nested (e.g.T14.hs), the plugin shouldoffer an action for each incompletetrigger for the innermostcasecaseexpression which the cursor is on;->or its unicode counterpart, honoring the-XUnicodeSyntaxflag or previous patterns.¹ Not really all, we don't want to split an all
Ints, for instance, nor on an actualdatawith 100 ctors, presumably. Or do we?