intellij-haskell icon indicating copy to clipboard operation
intellij-haskell copied to clipboard

Case split feature

Open 0xd34df00d opened this issue 4 years ago β€’ 18 comments

One thing I really miss from, say, Idris is case splitting a variable in a function definition or a case expression.

For instance, having a type like

data FooType = Foo | Bar Int

and having written

foo :: FooType -> Smth
foo x = undefined

doing the suggested "case split" on x should result in foo x = undefined replaced with the corresponding patterns like

foo :: FooType -> Smth
foo Foo = undefined
foo (Bar n) = undefined

Doing this on x in case x context should behave similarly.

Being able to add missing clauses would also be super cool!

I know this request sounds a bit vague, so please let me know if you'd like me to elaborate!

0xd34df00d avatar Jun 25 '20 04:06 0xd34df00d

Thanks for reporting!

I need to investigate this but I guess it’s doable for simple cases. The plugin lexer/parser is limited in recognizing Haskell syntax especially over multiple lines (I still need to continue with the branch which could solve this issue)

Are there more use cases you have in mind?

rikvdkleij avatar Jun 25 '20 17:06 rikvdkleij

I would be very hyped if this were included, this and automatic type insertion are the two things I miss the most. And I do mean miss - ghc-mod was able to do that, while seemingly every other tool that came after it was not.

The use cases are probably just "whenever you want to pattern match", without this feature I am forced to lookup the constructors, and even if I remember them I have to type them out - even though GHC knows exactly what should be there when my pattern match isn't exhaustive yet - some keyboard shortcut while hovering over a sum type would be a real timesaver.

While we're at it, in theory a similar argument could be made for filling in dummy typeclass methods in instance declarations, albeit this is probably not necessary as often.

mbwgh avatar Jul 17 '20 18:07 mbwgh

@mbwgh Thanks for your suggestion.

I once made support for typed holes but i noticed that's currently broken because the GHC message is changed. The idea was to type _ and then use IntelliJ's smart completion option to show the suggestions of GHC. Will fix that.

rikvdkleij avatar Jul 17 '20 19:07 rikvdkleij

@mbwgh

automatic type insertion are the two things I miss the most.

Can you elaborate automatic type insertion? In this context i understand automatically adding of data constructors.

rikvdkleij avatar Jul 19 '20 13:07 rikvdkleij

Sure thing. What I mean is simply being able to automatically (i.e. by using some key combination) insert the inferred type of a selected expression. Some examples:

someTopLevelConstant = 42

If I have the cursor over this top level definition, I would like to be able to add the type signature (Integer or Num a => a depending on whether the monomorphism restriction is active) inferred by ghc. This would ideally also be a quick-fix since there would be a warning.

The harder cases would be adding the types for type-level holes like

someIOAction :: IO _
someIOAction = liftIO getLine

Something like this could occur when you have some highly polymorphic type and you want to hint at a simplified signature. Here, the IO would constrain the type of the monad, without it ghc would infer MonadIO m => m String.

Then there's of course adding type signatures to expression-level typed holes.

One more thing where it would be really nice to have is in situations like this:

process :: Num a => [(a, String)] -> Bool
process = foldl' go False
    where
        go = undefined

where you intend to fold over some structure and can't remember the type of the function argument. This one is probably more tricky because there is a difference if ScopedTypeVariables is enabled or not. Still, right now I have to type out the type annotation for go by hand, and if I want to query the type I have to hover over the go in the foldl' go False expression, since the hole in go = _ would be typed something like a.

There are probably more examples and maybe even better ones, but hopefully this conveys the intention. Thanks for taking this into consideration :)

mbwgh avatar Jul 19 '20 13:07 mbwgh

If I have the cursor over this top level definition, I would like to be able to add the type signature (Integer or Num a => a depending on whether the monomorphism restriction is active) inferred by ghc. This would ideally also be a quick-fix since there would be a warning.

That's already implemented but it depends on GHC messages. So you have to enable e.g. -Wall .

rikvdkleij avatar Jul 19 '20 15:07 rikvdkleij

That's already implemented but it depends on GHC messages. So you have to enable e.g. -Wall .

I will add default GHC options to the REPL soon so those quick fixes/intentions are by default enabled.

rikvdkleij avatar Jul 19 '20 18:07 rikvdkleij

I would say that the quickfix doesn't cover every use case I mentioned, so it would probably not allow me to annotate a let binding. Maybe indirectly via a typed hole, though.

In any case, thanks for letting me know about the quickfix that is already there.

Maybe I should investigate this in a little more detail and open a feature request if there really is some functionality missing, since obviously this is getting a little off-topic here.

mbwgh avatar Jul 20 '20 06:07 mbwgh

I would say that the quickfix doesn't cover every use case I mentioned,

Yes, it currently only works for top-level expressions.

Maybe I should investigate this in a little more detail and open a feature request if there really is some functionality missing, since obviously this is getting a little off-topic here.

πŸ‘

Btw, the next version of the plugin has improved support for code completion on typed holes (also on type level)

rikvdkleij avatar Jul 20 '20 18:07 rikvdkleij

Making some progress in implementing in case splitting

casesplit3

rikvdkleij avatar Aug 24 '20 18:08 rikvdkleij

Yay @rikvdkleij this looks awesome!

0xd34df00d avatar Aug 24 '20 18:08 0xd34df00d

@0xd34df00d

Doing this on x in case x context should behave similarly.

Can you elaborate on this?

I see 2 solutions:

testcase :: FooType -> String
testcase m = case m of
  x -> undefined

or

testcase2 :: FooType -> String
testcase2 = _

The first one on the x. For this solution x -> undefined needs to be on new line to make it more simple to implement. The second one does generate the whole case expression within lambda, is it bit more complex to implement but I think more practical and solid.

rikvdkleij avatar Sep 10 '20 16:09 rikvdkleij

@rikvdkleij what I had in mind is the first option β€” just having an arbitrary case, not necessarily the top-level term on the RHS of a function. For instance, I can easily think of foo x = bar $ case x of ..., where case-splitting on x is still desirable. Or it could even be some complex expression like case foo (bar baz) meh of ..., but I'm not sure if you want to support that, cause it might require some non-trivial type inference in the general case. Anyway I think it's your first solution.

And even if it requires me to format the code in a certain way, then I'm surely fine with it.

BTW idris' editor integration (that gave me inspiration for this request) has two separate commands β€” one for generating a case expression (akin to your second solution, but it leaves a hole for the expression to case-split on), and another one for case-splitting on any variable (so you'd place the cursor on x in x -> undefined and issue that command). Not sure if it's the best way to implement this, but just some relevant prior art.

0xd34df00d avatar Sep 12 '20 23:09 0xd34df00d

@0xd34df00d Implemented in beta79. I'm curious if it works for you πŸ˜„

rikvdkleij avatar Sep 22 '20 19:09 rikvdkleij

@mbwgh In beta79 I have added default GHC options to REPL (which can be changed in Haskell Settings)

rikvdkleij avatar Sep 22 '20 19:09 rikvdkleij

@0xd34df00d Improved top-level case splitting support in beta80.

rikvdkleij avatar Sep 26 '20 14:09 rikvdkleij

@rikvdkleij thanks! Just got my hands on trying this out β€” works like a charm for a few limited examples I've tried! Thanks again for implementing this!

0xd34df00d avatar Oct 06 '20 20:10 0xd34df00d

@0xd34df00d Thanks for letting me know! Great to hear that it works for you.

Probably it will not work for all cases. You can always report those. I will keep improving the plugin.

rikvdkleij avatar Oct 07 '20 13:10 rikvdkleij