Fix the plugin (#62)

This commit is contained in:
Sandy Maguire 2019-05-23 17:05:32 -04:00 committed by GitHub
parent 882cd80abf
commit feacd2a001
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 68 additions and 14 deletions

View File

@ -33,6 +33,8 @@ module Polysemy.Plugin.Fundep (fundepPlugin) where
import Class import Class
import CoAxiom import CoAxiom
import Control.Monad import Control.Monad
import Data.Bifunctor
import Data.List
import Data.Maybe import Data.Maybe
import FastString (fsLit) import FastString (fsLit)
import GHC (ModuleName) import GHC (ModuleName)
@ -61,10 +63,10 @@ fundepPlugin = TcPlugin
allMonadEffectConstraints :: Class -> [Ct] -> [(CtLoc, (Type, Type, Type))] allMonadEffectConstraints :: Class -> [Ct] -> [(CtLoc, (Type, Type, Type))]
allMonadEffectConstraints cls cts = allMonadEffectConstraints cls cts =
[ (ctLoc cd, (effName, eff, r)) [ (ctLoc cd, (effName, eff, r))
| cd@CDictCan{cc_class = cls', cc_tyargs = [_, r, eff]} <- cts | cd@CDictCan{cc_class = cls', cc_tyargs = [_, r, eff]} <- cts
, cls == cls' , cls == cls'
, let effName = getEffName eff , let effName = getEffName eff
] ]
singleListToJust :: [a] -> Maybe a singleListToJust :: [a] -> Maybe a
singleListToJust [a] = Just a singleListToJust [a] = Just a
@ -81,10 +83,6 @@ getEffName :: Type -> Type
getEffName t = fst $ splitAppTys t getEffName t = fst $ splitAppTys t
-- isTyVar :: Type -> Bool
-- isTyVar = isJust . getTyVar_maybe
canUnify :: Type -> Type -> Bool canUnify :: Type -> Type -> Bool
canUnify wanted given = canUnify wanted given =
let (w, ws) = splitAppTys wanted let (w, ws) = splitAppTys wanted
@ -96,26 +94,39 @@ canUnify wanted given =
mkWanted :: Bool -> CtLoc -> Type -> Type -> TcPluginM (Maybe Ct) mkWanted :: Bool -> CtLoc -> Type -> Type -> TcPluginM (Maybe Ct)
mkWanted mustUnify loc wanted given = do mkWanted mustUnify loc wanted given =
if (not mustUnify || canUnify wanted given) if (not mustUnify || canUnify wanted given)
then do then do
(ev, _) <- unsafeTcPluginTcM $ runTcSDeriveds $ newWantedEq loc Nominal wanted given (ev, _) <- unsafeTcPluginTcM $ runTcSDeriveds $ newWantedEq loc Nominal wanted given
pure $ Just (CNonCanonical ev) pure $ Just $ CNonCanonical ev
else else
pure Nothing pure Nothing
thd :: (a, b, c) -> c
thd (_, _, c) = c
countLength :: (a -> a -> Bool) -> [a] -> [(a, Int)]
countLength eq as =
let grouped = groupBy eq as
in zipWith (curry $ bimap head length) grouped grouped
solveFundep :: Class -> [Ct] -> [Ct] -> [Ct] -> TcPluginM TcPluginResult solveFundep :: Class -> [Ct] -> [Ct] -> [Ct] -> TcPluginM TcPluginResult
solveFundep _ _ _ [] = pure $ TcPluginOk [] []
solveFundep effCls giv _ want = do solveFundep effCls giv _ want = do
let wantedEffs = allMonadEffectConstraints effCls want let wantedEffs = allMonadEffectConstraints effCls want
let givenEffs = snd <$> allMonadEffectConstraints effCls giv givenEffs = snd <$> allMonadEffectConstraints effCls giv
eqs <- forM wantedEffs $ \(loc, e@(_, eff, r)) -> num_wanteds_by_r = countLength eqType $ fmap (thd . snd) wantedEffs
must_unify r =
let Just num_wanted = find (eqType r . fst) num_wanteds_by_r
in snd num_wanted /= 1
eqs <- forM wantedEffs $ \(loc, e@(_, eff, r)) -> do
case findMatchingEffectIfSingular e givenEffs of case findMatchingEffectIfSingular e givenEffs of
Nothing -> do Nothing -> do
case splitAppTys r of case splitAppTys r of
(_, [_, eff', _]) -> mkWanted False loc eff eff' (_, [_, eff', _]) -> mkWanted (must_unify r) loc eff eff'
_ -> pure Nothing _ -> pure Nothing
Just eff' -> mkWanted True loc eff eff' Just eff' -> mkWanted True loc eff eff'
return (TcPluginOk [] (catMaybes eqs)) pure $ TcPluginOk [] $ catMaybes eqs

View File

@ -0,0 +1,43 @@
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE BlockArguments #-}
{-# OPTIONS_GHC -fplugin=Polysemy.Plugin #-}
module ExampleSpec where
import Polysemy
import Polysemy.Error
import Polysemy.Input
import Polysemy.Output
import Polysemy.Resource
import Test.Hspec
data Teletype m a where
ReadTTY :: Teletype m String
WriteTTY :: String -> Teletype m ()
makeSem ''Teletype
runTeletypeIO :: Member (Lift IO) r => Sem (Teletype ': r) a -> Sem r a
runTeletypeIO = interpret $ \case
ReadTTY -> sendM getLine
WriteTTY msg -> sendM $ putStrLn msg
data CustomException = ThisException | ThatException deriving Show
program :: Members '[Teletype, Resource, Error CustomException] r => Sem r ()
program = catch @CustomException work $ \e -> writeTTY ("Caught " ++ show e)
where work = bracket (readTTY) (const $ writeTTY "exiting bracket") $ \i -> do
writeTTY "entering bracket"
case i of
"explode" -> throw ThisException
"weird stuff" -> writeTTY i >> throw ThatException
_ -> writeTTY i >> writeTTY "no exceptions"
foo :: IO (Either CustomException ())
foo = (runM .@ runResource .@@ runErrorInIO @CustomException) $ runTeletypeIO program
spec :: Spec
spec = describe "example" $ do
it "should compile!" $ do
True `shouldBe` True