packages feed

polysemy-plugin 0.2.0.3 → 0.2.1.0

raw patch · 4 files changed

+76/−26 lines, 4 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Polysemy.Plugin.Fundep: instance GHC.Classes.Eq Polysemy.Plugin.Fundep.SolveContext
+ Polysemy.Plugin.Fundep: instance GHC.Classes.Ord Polysemy.Plugin.Fundep.SolveContext
+ Polysemy.Plugin.Fundep: instance GHC.Show.Show Polysemy.Plugin.Fundep.SolveContext

Files

ChangeLog.md view
@@ -1,5 +1,10 @@ # Changelog for polysemy-plugin +## 0.2.1.0 (2019-06-14)++- Greatly improved the plugin's ability to unify polymorphic types when running+    interpreters.+ ## 0.2.0.3 (2019-06-13)  - Fixed a bug where the plugin could (incorrectly) loop indefinitely attempting
polysemy-plugin.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: 65e9d7abb09d631f2fc5d94e0710c5770b02658b1e699b39a00e2f673c81beb1+-- hash: e4dc8b1a6c6d69287ad57b504d173944aea70ea24df70dab669ea1f70715c3b0  name:           polysemy-plugin-version:        0.2.0.3+version:        0.2.1.0 synopsis:       Disambiguate obvious uses of effects. description:    Please see the README on GitHub at <https://github.com/isovector/polysemy/tree/master/polysemy-plugin#readme> category:       Polysemy
src/Polysemy/Plugin/Fundep.hs view
@@ -34,8 +34,10 @@  import           Class import           CoAxiom+import           Control.Applicative import           Control.Monad import           Data.Bifunctor+import           Data.Bool import           Data.Function (on) import           Data.IORef import           Data.List@@ -89,34 +91,63 @@ getEffName t = fst $ splitAppTys t  -canUnify :: Type -> Type -> Bool-canUnify wanted given =-  let (w, ws) = splitAppTys wanted-      (g, gs) = splitAppTys given-   in (&& eqType w g) . flip all (zip ws gs) $ \(wt, gt) ->-     or [ isTyVarTy wt-        , eqType wt gt-        , canUnify wt gt-        ]+canUnifyRecursive :: SolveContext -> Type -> Type -> Bool+canUnifyRecursive solve_ctx = go True+  where+    -- It's only OK to solve a polymorphic "given" if we're in the context of+    -- an interpreter, because it's not really a given!+    poly_given_ok :: Bool+    poly_given_ok =+      case solve_ctx of+        InterpreterUse _ -> True+        FunctionDef      -> False +    -- On the first go around, we don't want to unify effects with tyvars, but+    -- we _do_ want to unify their arguments, thus 'is_first'.+    go :: Bool -> Type -> Type -> Bool+    go is_first wanted given =+      let (w, ws) = splitAppTys wanted+          (g, gs) = splitAppTys given+       in (&& bool (canUnify poly_given_ok) eqType is_first w g)+        . flip all (zip ws gs)+        $ \(wt, gt) -> canUnify poly_given_ok wt gt || go False wt gt ++canUnify :: Bool -> Type -> Type -> Bool+canUnify poly_given_ok wt gt =+  or [ isTyVarTy wt+     , isTyVarTy gt && poly_given_ok+     , eqType wt gt+     ]+++------------------------------------------------------------------------------+-- | Like 'Control.Monad.when', but in the context of an 'Alternative'.+whenA+    :: (Monad m, Alternative z)+    => Bool+    -> m a+    -> m (z a)+whenA False _ = pure empty+whenA True ma = fmap pure ma++ mkWanted-    :: Bool+    :: SolveContext     -> CtLoc     -> Type     -> Type     -> TcPluginM (Maybe ( (OrdType, OrdType)  -- the types we want to unify                         , Ct                  -- the constraint                         ))-mkWanted must_unify loc wanted given =-  if (not must_unify || canUnify wanted given)-     then do-       (ev, _) <- unsafeTcPluginTcM $ runTcSDeriveds $ newWantedEq loc Nominal wanted given-       pure $ Just ( (OrdType wanted, OrdType given)-                   , CNonCanonical ev-                   )-     else-       pure Nothing+mkWanted solve_ctx loc wanted given =+  whenA (not (mustUnify solve_ctx) || canUnifyRecursive solve_ctx wanted given) $ do+    (ev, _) <- unsafeTcPluginTcM+             . runTcSDeriveds+             $ newWantedEq loc Nominal wanted given+    pure ( (OrdType wanted, OrdType given)+         , CNonCanonical ev+         )  thd :: (a, b, c) -> c thd (_, _, c) = c@@ -142,7 +173,22 @@   compare = nonDetCmpType `on` getOrdType  +------------------------------------------------------------------------------+-- | The context in which we're attempting to solve a constraint.+data SolveContext+  = -- | In the context of a function definition.+    FunctionDef+    -- | In the context of running an interpreter. The 'Bool' corresponds to+    -- whether we are only trying to solve a single 'Member' constraint right+    -- now. If so, we *must* produce a unification wanted.+  | InterpreterUse Bool+  deriving (Eq, Ord, Show) +mustUnify :: SolveContext -> Bool+mustUnify FunctionDef = True+mustUnify (InterpreterUse b) = b++ solveFundep     :: (IORef (S.Set (OrdType, OrdType)), Class)     -> [Ct]@@ -162,9 +208,9 @@       case findMatchingEffectIfSingular e givenEffs of         Nothing -> do           case splitAppTys r of-            (_, [_, eff', _]) -> mkWanted (must_unify r) loc eff eff'+            (_, [_, eff', _]) -> mkWanted (InterpreterUse $ must_unify r) loc eff eff'             _                 -> pure Nothing-        Just eff' -> mkWanted True loc eff eff'+        Just eff' -> mkWanted FunctionDef loc eff eff'      already_emitted <- tcPluginIO $ readIORef ref     let new_wanteds = filter (not . flip S.member already_emitted . fst)
test/PluginSpec.hs view
@@ -132,9 +132,8 @@       flipShouldBe (Right @Bool (10 :: Float, True))  . run $ runError $ runState 0 errState    describe "Output effect" $ do-    it "should unify recursively" $ do-      -- TODO(sandy): This should unify even without the type app. Bug #95-      flipShouldBe 11 . sum @[] . fst . run . runFoldMapOutput id $ do+    it "should unify recursively with tyvars" $ do+      flipShouldBe 11 . sum . fst . run . runFoldMapOutput id $ do         output [1]         output $ replicate 2 5