Add HoleFitPlugins and RawHoleFits
-
Review changes -
-
Download -
Patches
-
Plain diff
This patch adds a new kind of plugin, Hole Fit Plugins. These plugins can change what candidates are considered when looking for valid hole fits, and add hole fits of their own. The type of a plugin is relatively simple,
type FitPlugin = TypedHole -> [HoleFit] -> TcM [HoleFit]
type CandPlugin = TypedHole -> [HoleFitCandidate] -> TcM [HoleFitCandidate]
data HoleFitPlugin = HoleFitPlugin { candPlugin :: CandPlugin
, fitPlugin :: FitPlugin }
data TypedHole = TyH { relevantCts :: Cts -- ^ Any relevant Cts to the hole
, implics :: [Implication] -- ^ The nested implications of the hole
-- with the innermost implication first.
, holeCt :: Maybe Ct -- ^ The hole constraint itself, if available. }
This allows users and plugin writers to interact with the candidates and
fits as they wish, even going as far as to allow them to reimplement the
current functionality (since TypedHole
contains all the relevant
information).
As an example, consider the following plugin:
module HolePlugin where
import GhcPlugins
import TcHoleErrors
import Data.List (intersect, stripPrefix)
import RdrName (importSpecModule)
import TcRnTypes
import System.Process
plugin :: Plugin
plugin = defaultPlugin { holeFitPlugin = hfp, pluginRecompile = purePlugin }
hfp :: [CommandLineOption] -> Maybe HoleFitPlugin
hfp opts = Just (HoleFitPlugin (candP opts) (fp opts))
toFilter :: Maybe String -> Maybe String
toFilter = flip (>>=) (stripPrefix "_module_")
replace :: Eq a => a -> a -> [a] -> [a]
replace match repl str = replace' [] str
where
replace' sofar (x:xs) | x == match = replace' (repl:sofar) xs
replace' sofar (x:xs) = replace' (x:sofar) xs
replace' sofar [] = reverse sofar
-- | This candidate plugin filters the candidates by module,
-- using the name of the hole as module to search in
candP :: [CommandLineOption] -> CandPlugin
candP _ hole cands =
do let he = case holeCt hole of
Just (CHoleCan _ h) -> Just (occNameString $ holeOcc h)
_ -> Nothing
case toFilter he of
Just undscModName -> do let replaced = replace '_' '.' undscModName
let res = filter (greNotInOpts [replaced]) cands
return $ res
_ -> return cands
where greNotInOpts opts (GreHFCand gre) = not $ null $ intersect (inScopeVia gre) opts
greNotInOpts _ _ = True
inScopeVia = map (moduleNameString . importSpecModule) . gre_imp
-- Yes, it's pretty hacky, but it is just an example :)
searchHoogle :: String -> IO [String]
searchHoogle ty = lines <$> (readProcess "hoogle" [(show ty)] [])
fp :: [CommandLineOption] -> FitPlugin
fp ("hoogle":[]) hole hfs =
do dflags <- getDynFlags
let tyString = showSDoc dflags . ppr . ctPred <$> holeCt hole
res <- case tyString of
Just ty -> liftIO $ searchHoogle ty
_ -> return []
return $ (take 2 $ map (RawHoleFit . text .("Hoogle says: " ++)) res) ++ hfs
fp _ _ hfs = return hfs
with this plugin available, you can compile the following file
{-# OPTIONS -fplugin=HolePlugin -fplugin-opt=HolePlugin:hoogle #-}
module Main where
import Prelude hiding (head, last)
import Data.List (head, last)
t :: [Int] -> Int
t = _module_Prelude
g :: [Int] -> Int
g = _module_Data_List
main :: IO ()
main = print $ t [1,2,3]
and get the following output:
Main.hs:14:5: error:
• Found hole: _module_Prelude :: [Int] -> Int
Or perhaps ‘_module_Prelude’ is mis-spelled, or not in scope
• In the expression: _module_Prelude
In an equation for ‘t’: t = _module_Prelude
• Relevant bindings include
t :: [Int] -> Int (bound at Main.hs:14:1)
Valid hole fits include
Hoogle says: GHC.List length :: [a] -> Int
Hoogle says: GHC.OldList length :: [a] -> Int
t :: [Int] -> Int (bound at Main.hs:14:1)
g :: [Int] -> Int (bound at Main.hs:17:1)
length :: forall (t :: * -> *) a. Foldable t => t a -> Int
with length @[] @Int
(imported from ‘Prelude’ at Main.hs:5:1-34
(and originally defined in ‘Data.Foldable’))
maximum :: forall (t :: * -> *) a. (Foldable t, Ord a) => t a -> a
with maximum @[] @Int
(imported from ‘Prelude’ at Main.hs:5:1-34
(and originally defined in ‘Data.Foldable’))
(Some hole fits suppressed; use -fmax-valid-hole-fits=N or -fno-max-valid-hole-fits)
|
14 | t = _module_Prelude
| ^^^^^^^^^^^^^^^
Main.hs:17:5: error:
• Found hole: _module_Data_List :: [Int] -> Int
Or perhaps ‘_module_Data_List’ is mis-spelled, or not in scope
• In the expression: _module_Data_List
In an equation for ‘g’: g = _module_Data_List
• Relevant bindings include
g :: [Int] -> Int (bound at Main.hs:17:1)
Valid hole fits include
Hoogle says: GHC.List length :: [a] -> Int
Hoogle says: GHC.OldList length :: [a] -> Int
g :: [Int] -> Int (bound at Main.hs:17:1)
head :: forall a. [a] -> a
with head @Int
(imported from ‘Data.List’ at Main.hs:7:19-22
(and originally defined in ‘GHC.List’))
last :: forall a. [a] -> a
with last @Int
(imported from ‘Data.List’ at Main.hs:7:25-28
(and originally defined in ‘GHC.List’))
|
17 | g = _module_Data_List
This relatively simple plugin has two functions, as an example of what
is possible to do with hole fit plugins. The candidate plugin starts by
filtering the candidates considered by module, indicated by the name of
the hole (_module_Data_List
). The second function is in the fit
plugin, where the plugin invokes a local hoogle instance to search by
the type of the hole.
By adding the RawHoleFit
type, we can also allow these completely free
suggestions, used in the plugin above to display fits found by Hoogle.
Of course, the syntax here is up for debate, but hole fit plugins allow us to experiment relatively easily with ways to interact with typed-holes without having to dig deep into GHC.
Previously Differential Revision https://phabricator.haskell.org/D5373
Merge request reports
- version 23aa034a30
- version 22cff7d15b
- version 2167563027
- version 203559a11e
- version 190ade0771
- version 18adb25334
- version 1736239989
- version 164617c0f7
- version 15ee2902c1
- version 14e39d3d3a
- version 130acced34
- version 1235eca34c
- version 118fe4f8a3
- version 10ce27e677
- version 9f224488e
- version 85d046094
- version 702c03cfb
- version 69e6ea3a7
- version 5777c5369
- version 4b42ee9c9
- version 3e9fc90b8
- version 20c90738f
- version 128909a1a
- master (base)
- latest versionc311277b1 commit,
- version 23aa034a302 commits,
- version 22cff7d15b19 commits,
- version 216756302718 commits,
- version 203559a11e17 commits,
- version 190ade077116 commits,
- version 18adb2533415 commits,
- version 173623998914 commits,
- version 164617c0f713 commits,
- version 15ee2902c112 commits,
- version 14e39d3d3a5 commits,
- version 130acced344 commits,
- version 1235eca34c3 commits,
- version 118fe4f8a32 commits,
- version 10ce27e6771 commit,
- version 9f224488e1 commit,
- version 85d0460941 commit,
- version 702c03cfb1 commit,
- version 69e6ea3a71 commit,
- version 5777c53691 commit,
- version 4b42ee9c91 commit,
- version 3e9fc90b81 commit,
- version 20c90738f1 commit,
- version 128909a1a1 commit,
- Side-by-side
- Inline