-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | Disambiguate obvious uses of effects. -- -- Please see the README on GitHub at -- https://github.com/isovector/polysemy/tree/master/polysemy-plugin#readme @package polysemy-plugin @version 0.2.5.2 module Polysemy.Plugin.Fundep.Stuff -- | All of the things from "polysemy" that we need access to in the -- plugin. When l ~ 'Locations, each of these is just a pair of -- strings. When l ~ 'Things, it's actually references to the -- stuff. data PolysemyStuff (l :: LookupState) PolysemyStuff :: ThingOf l Class -> ThingOf l TyCon -> ThingOf l TyCon -> ThingOf l TyCon -> PolysemyStuff (l :: LookupState) [findClass] :: PolysemyStuff (l :: LookupState) -> ThingOf l Class [semTyCon] :: PolysemyStuff (l :: LookupState) -> ThingOf l TyCon [ifStuckTyCon] :: PolysemyStuff (l :: LookupState) -> ThingOf l TyCon [locateEffectTyCon] :: PolysemyStuff (l :: LookupState) -> ThingOf l TyCon -- | Data kind for ThingOf. data LookupState Locations :: LookupState Things :: LookupState -- | Lookup all of the PolysemyStuff. polysemyStuff :: TcPluginM (PolysemyStuff 'Things) instance Polysemy.Plugin.Fundep.Stuff.CanLookup Class.Class instance Polysemy.Plugin.Fundep.Stuff.CanLookup TyCon.TyCon module Polysemy.Plugin.Fundep.Unification -- | The context in which we're attempting to solve a constraint. data SolveContext -- | In the context of a function definition. FunctionDef :: SolveContext -- | 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 -> SolveContext -- | Depending on the context in which we're solving a constraint, we may -- or may not want to force a unification of effects. For example, when -- defining user code whose type is Member (State Int) r => -- ..., if we see get :: Sem r s, we should unify s ~ -- Int. mustUnify :: SolveContext -> Bool -- | Determine whether or not two effects are unifiable. This is nuanced. -- -- There are several cases: -- --
    --
  1. [W] ∀ e1. e1 [G] ∀ e2. e2 Always fails, because we never want to -- unify two effects if effect names are polymorphic.
  2. --
  3. [W] State s [G] State Int Always succeeds. It's safe to take our -- given as a fundep annotation.
  4. --
  5. [W] State Int [G] State s (when the [G] is a given that comes from -- a type signature)
  6. --
-- -- This should fail, because it means we wrote the type signature -- Member (State s) r => ..., but are trying to use -- s as an Int. Clearly bogus! -- --
    --
  1. [W] State Int [G] State s (when the [G] was generated by running -- an interpreter)
  2. --
-- -- Sometimes OK, but only if the [G] is the only thing we're trying to -- solve right now. Consider the case: -- -- runState 5 $ pure @(Sem (State Int ': r)) () -- -- Here we have [G] forall a. Num a => State a and [W] State Int. -- Clearly the typechecking should flow "backwards" here, out of the row -- and into the type of runState. -- -- What happens if there are multiple [G]s in scope for the same -- r? Then we'd emit multiple unification constraints for the -- same effect but with different polymorphic variables, which would -- unify a bunch of effects that shouldn't be! canUnifyRecursive :: SolveContext -> Type -> Type -> Bool -- | A non-recursive version of canUnifyRecursive. canUnify :: Bool -> Type -> Type -> Bool -- | A wrapper for two types that we want to say have been unified. data Unification Unification :: OrdType -> OrdType -> Unification [_unifyLHS] :: Unification -> OrdType [_unifyRHS] :: Unification -> OrdType -- | Types don't have Eq or Ord instances by default, -- even though there are functions in GHC that implement these -- operations. This newtype gives us those instances. newtype OrdType OrdType :: Type -> OrdType [getOrdType] :: OrdType -> Type -- | Filter out the unifications we've already emitted, and then give back -- the things we should put into the S.Set Unification, and the -- new constraints we should emit. unzipNewWanteds :: Set Unification -> [(Unification, Ct)] -> ([Unification], [Ct]) instance GHC.Show.Show Polysemy.Plugin.Fundep.Unification.SolveContext instance GHC.Classes.Ord Polysemy.Plugin.Fundep.Unification.SolveContext instance GHC.Classes.Eq Polysemy.Plugin.Fundep.Unification.SolveContext instance GHC.Classes.Ord Polysemy.Plugin.Fundep.Unification.Unification instance GHC.Classes.Eq Polysemy.Plugin.Fundep.Unification.Unification instance GHC.Classes.Eq Polysemy.Plugin.Fundep.Unification.OrdType instance GHC.Classes.Ord Polysemy.Plugin.Fundep.Unification.OrdType module Polysemy.Plugin.Fundep.Utils -- | Returns the head of the list iff there is exactly one element. singleListToJust :: [a] -> Maybe a -- | Like when, but in the context of an Alternative. whenA :: (Monad m, Alternative z) => Bool -> m a -> m (z a) -- | Count the number of times a is present in the list. countLength :: Eq a => [a] -> [(a, Int)] module Polysemy.Plugin.Fundep fundepPlugin :: TcPlugin -- | A typechecker plugin that can disambiguate "obvious" uses of effects -- in Polysemy. -- -- Example: -- -- Consider the following program: -- --
--   foo :: Member (State Int) r => Sem r ()
--   foo = put 10
--   
-- -- What does this program do? Any human will tell you that it changes the -- state of the Int to 10, which is clearly what's meant. -- -- Unfortunately, Polysemy can't work this out on its own. Its reasoning -- is "maybe you wanted to change some other State effect which is -- also a Num, but you just forgot to add a Member -- constraint for it." -- -- This is obviously insane, but it's the way the cookie crumbles. -- Plugin is a typechecker plugin which will disambiguate the -- above program (and others) so the compiler will do what you want. -- -- Usage: -- -- Add the following line to your package configuration: -- --
--   ghc-options: -fplugin=Polysemy.Plugin
--   
-- -- Limitations: -- -- The Plugin will only disambiguate effects if there is exactly -- one relevant constraint in scope. For example, it will not -- disambiguate the following program: -- --
--   bar :: Members '[ State Int
--                   , State Double
--                   ] r => Sem r ()
--   bar = put 10
--   
-- -- because it is now unclear whether you're attempting to set the -- Int or the Double. Instead, you can manually write a -- type application in this case. -- --
--   bar :: Members '[ State Int
--                   , State Double
--                   ] r => Sem r ()
--   bar = put @Int 10
--   
module Polysemy.Plugin plugin :: Plugin