Case splitting plugin.
A GHC plugin that performs automatic case splitting
auto-split 🤸
A GHC plugin that performs automatic case splitting.
Usage
This plugin is intended to be used with GHCi or adjacent utilities such as ghcid
and ghciwatch
as a developement tool, not as a package dependency. Here is an example command for starting a REPL for a stack project with the auto-split
plugin enabled (you may need to add auto-split
to your extra-deps
first):
stack repl my-project --package auto-split --ghci-options='-fplugin AutoSplit'
likewise for a cabal project (you may need to run cabal update
first):
cabal repl my-project --build-depends auto-split --repl-options='-fplugin AutoSplit'
With the plugin enabled, use SPLIT
in a pattern match for a case statement or function declaration. Compiling the module will then result in that pattern match being expanded to cover all missing cases.
For example, compiling this module
module Foo where
foo :: Maybe Bool -> String
foo x = case x of
SPLIT -> undefined
results in the module file being updated to
module Foo where
foo :: Maybe Bool -> String
foo x = case x of
Just _ -> undefined
Nothing -> undefined
if the module is then changed to
module Foo where
foo :: Maybe Bool -> String
foo x = case x of
Just SPLIT -> "foo"
Nothing -> "bar"
compiling will result in
module Foo where
foo :: Maybe Bool -> String
foo x = case x of
Just True -> "foo"
Just False -> "foo"
Nothing -> "bar"
The SPLIT
pattern can be used in this way anywhere a pattern match group occurs: case statements, lamba case, or function declarations.
If case splitting results in constructors or patterns that are not in scope, they will be qualified with NOT_IN_SCOPE
.
Compilation will abort when case splitting occurs since the module file has been updated. This is done by having GHC emit a custom error. This error does not indicate that something went wrong.
Bonus feature: Automatic record field enumeration
Using FIELDS
with an incomplete record initialization will result in the missing fields being added. For example, compiling this module
mkFoo = FIELDS Foo { }
will result in
mkFoo = Foo { field1 :: _, field2 :: _ }
Known limitations
- A module must pass the type checker for splitting to occur, unless the
-fdefer-type-errors
GHC flag is used. - Using
SPLIT
in a pattern match will insert patterns for all missing cases in the group. It doesn't restrict to the position whereSPLIT
is used. - If the pattern match where
SPLIT
is being used contains a catch-all wildcard case then the plugin will have no effect because there are no missing patterns. - Doesn't work well with the view patterns syntax extension
- Doesn't apply to code inside CPP conditional blocks
- The plugin only supports certain GHC versions with the intent of supporting the four latest release series, i.e.
9.6.*
thru9.12.*
. The cabal file indicates the currently supported versions.