polysemy 0.1.2.1 → 0.2.0.0
raw patch · 9 files changed
+118/−89 lines, 9 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
- Polysemy: infixl 9 .@@
- Polysemy.Internal: infixl 9 .@@
+ Polysemy: infixl 8 .@@
+ Polysemy.Internal: infixl 8 .@@
+ Polysemy.Internal: raiseUnder :: forall e2 e1 r a. Sem (e1 : r) a -> Sem (e1 : (e2 : r)) a
+ Polysemy.Internal: raiseUnder2 :: forall e2 e3 e1 r a. Sem (e1 : r) a -> Sem (e1 : (e2 : (e3 : r))) a
+ Polysemy.Internal: raiseUnder3 :: forall e2 e3 e4 e1 r a. Sem (e1 : r) a -> Sem (e1 : (e2 : (e3 : (e4 : r)))) a
+ Polysemy.Internal.Union: weakenUnder :: Union (e1 : r) m a -> Union (e1 : (e2 : r)) m a
+ Polysemy.Internal.Union: weakenUnder2 :: Union (e1 : r) m a -> Union (e1 : (e2 : (e3 : r))) m a
+ Polysemy.Internal.Union: weakenUnder3 :: Union (e1 : r) m a -> Union (e1 : (e2 : (e3 : (e4 : r)))) m a
- Polysemy.Error: catch :: forall e_al12 a_al14. forall r_alcS. Member (Error e_al12) r_alcS => Sem r_alcS a_al14 -> (e_al12 -> Sem r_alcS a_al14) -> Sem r_alcS a_al14
+ Polysemy.Error: catch :: forall e_akCX a_akCZ. forall r_akON. Member (Error e_akCX) r_akON => Sem r_akON a_akCZ -> (e_akCX -> Sem r_akON a_akCZ) -> Sem r_akON a_akCZ
- Polysemy.Error: throw :: forall e_al0Z a_al11. forall r_alcR. Member (Error e_al0Z) r_alcR => e_al0Z -> Sem r_alcR a_al11
+ Polysemy.Error: throw :: forall e_akCU a_akCW. forall r_akOM. Member (Error e_akCU) r_akOM => e_akCU -> Sem r_akOM a_akCW
- Polysemy.Input: input :: forall i_aqBI. forall r_aqCt. Member (Input i_aqBI) r_aqCt => Sem r_aqCt i_aqBI
+ Polysemy.Input: input :: forall i_aqdP. forall r_aqeA. Member (Input i_aqdP) r_aqeA => Sem r_aqeA i_aqdP
- Polysemy.Output: output :: forall o_apZS. forall r_aq0C. Member (Output o_apZS) r_aq0C => o_apZS -> Sem r_aq0C ()
+ Polysemy.Output: output :: forall o_apBS. forall r_apCC. Member (Output o_apBS) r_apCC => o_apBS -> Sem r_apCC ()
- Polysemy.Random: random :: forall x_apnH. Random x_apnH => forall r_app9. Member Random r_app9 => Sem r_app9 x_apnH
+ Polysemy.Random: random :: forall x_aoZH. Random x_aoZH => forall r_ap19. Member Random r_ap19 => Sem r_ap19 x_aoZH
- Polysemy.Random: randomR :: forall x_apnJ. Random x_apnJ => forall r_appa. Member Random r_appa => (x_apnJ, x_apnJ) -> Sem r_appa x_apnJ
+ Polysemy.Random: randomR :: forall x_aoZJ. Random x_aoZJ => forall r_ap1a. Member Random r_ap1a => (x_aoZJ, x_aoZJ) -> Sem r_ap1a x_aoZJ
- Polysemy.Reader: ask :: forall i_aqWk. forall r_aqXC. Member (Reader i_aqWk) r_aqXC => Sem r_aqXC i_aqWk
+ Polysemy.Reader: ask :: forall i_aqyr. forall r_aqzJ. Member (Reader i_aqyr) r_aqzJ => Sem r_aqzJ i_aqyr
- Polysemy.Reader: local :: forall i_aqWm a_aqWo. forall r_aqXD. Member (Reader i_aqWm) r_aqXD => (i_aqWm -> i_aqWm) -> Sem r_aqXD a_aqWo -> Sem r_aqXD a_aqWo
+ Polysemy.Reader: local :: forall i_aqyt a_aqyv. forall r_aqzK. Member (Reader i_aqyt) r_aqzK => (i_aqyt -> i_aqyt) -> Sem r_aqzK a_aqyv -> Sem r_aqzK a_aqyv
- Polysemy.Resource: bracket :: forall a_XnjS b_anjS. forall r_ankS. Member Resource r_ankS => Sem r_ankS a_XnjS -> (a_XnjS -> Sem r_ankS ()) -> (a_XnjS -> Sem r_ankS b_anjS) -> Sem r_ankS b_anjS
+ Polysemy.Resource: bracket :: forall a_XmVQ b_amVQ. forall r_amWQ. Member Resource r_amWQ => Sem r_amWQ a_XmVQ -> (a_XmVQ -> Sem r_amWQ ()) -> (a_XmVQ -> Sem r_amWQ b_amVQ) -> Sem r_amWQ b_amVQ
- Polysemy.State: get :: forall s_anF8. forall r_anGj. Member (State s_anF8) r_anGj => Sem r_anGj s_anF8
+ Polysemy.State: get :: forall s_anh6. forall r_anih. Member (State s_anh6) r_anih => Sem r_anih s_anh6
- Polysemy.State: put :: forall s_anFa. forall r_anGk. Member (State s_anFa) r_anGk => s_anFa -> Sem r_anGk ()
+ Polysemy.State: put :: forall s_anh8. forall r_anii. Member (State s_anh8) r_anii => s_anh8 -> Sem r_anii ()
- Polysemy.Trace: trace :: forall r_arim. Member Trace r_arim => String -> Sem r_arim ()
+ Polysemy.Trace: trace :: forall r_aqUt. Member Trace r_aqUt => String -> Sem r_aqUt ()
- Polysemy.Writer: censor :: forall o_arI2 a_arI4. forall r_arJT. Member (Writer o_arI2) r_arJT => (o_arI2 -> o_arI2) -> Sem r_arJT a_arI4 -> Sem r_arJT a_arI4
+ Polysemy.Writer: censor :: forall o_ark9 a_arkb. forall r_arm0. Member (Writer o_ark9) r_arm0 => (o_ark9 -> o_ark9) -> Sem r_arm0 a_arkb -> Sem r_arm0 a_arkb
- Polysemy.Writer: listen :: forall o_arHZ a_XrI2. forall r_arJS. Member (Writer o_arHZ) r_arJS => Sem r_arJS a_XrI2 -> Sem r_arJS (o_arHZ, a_XrI2)
+ Polysemy.Writer: listen :: forall o_ark6 a_Xrk9. forall r_arlZ. Member (Writer o_ark6) r_arlZ => Sem r_arlZ a_Xrk9 -> Sem r_arlZ (o_ark6, a_Xrk9)
- Polysemy.Writer: tell :: forall o_arHX. forall r_arJR. Member (Writer o_arHX) r_arJR => o_arHX -> Sem r_arJR ()
+ Polysemy.Writer: tell :: forall o_ark4. forall r_arlY. Member (Writer o_ark4) r_arlY => o_ark4 -> Sem r_arlY ()
Files
- ChangeLog.md +13/−0
- polysemy.cabal +4/−3
- src/Polysemy.hs +1/−3
- src/Polysemy/Internal.hs +49/−2
- src/Polysemy/Internal/Combinators.hs +4/−4
- src/Polysemy/Internal/CustomErrors.hs +2/−1
- src/Polysemy/Internal/TH/Performance.hs +5/−76
- src/Polysemy/Internal/Union.hs +24/−0
- test/HigherOrderSpec.hs +16/−0
ChangeLog.md view
@@ -1,5 +1,11 @@ # Changelog for polysemy +## 0.2.0.0 (2019-05-23)++- Fixed a serious bug in `interpretH` and friends, where higher-order effects+ would always be run with the current interpreter.+- Lower precedence of `.@` and `.@@` to 8, from 9+ ## 0.1.2.1 (2019-05-18) - Give explicit package bounds for dependencies@@ -27,4 +33,11 @@ - Initial release ## Unreleased changes++- **NEEDS MAJOR REVISION**: no longer require `inlineRecursiveCalls` --- the+ plugin does it automatically when compiling with `-O2`+- Deprecated `inlineRecursiveCalls`; slated for removal in the next version+- **NEEDS MAJOR PLUGIN REVISION**: plugin now automatically inlines recursive+ calls+
polysemy.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: 07720201673d182e7f285d795076b886bb48fb10b1704776922e431bf41b6c27+-- hash: 9a2044113434528f0539fb410b356387676cc468eefb618a666c1246379b43da name: polysemy-version: 0.1.2.1+version: 0.2.0.0 synopsis: Higher-order, low-boilerplate, zero-cost free monads. description: Please see the README on GitHub at <https://github.com/isovector/polysemy#readme> category: Language@@ -68,7 +68,7 @@ hs-source-dirs: src default-extensions: DataKinds DeriveFunctor FlexibleContexts GADTs LambdaCase PolyKinds RankNTypes ScopedTypeVariables StandaloneDeriving TypeApplications TypeOperators TypeFamilies UnicodeSyntax- ghc-options: -O2 -Wall+ ghc-options: -Wall build-depends: base >=4.7 && <5 , mtl >=2.2.2 && <3@@ -89,6 +89,7 @@ main-is: Main.hs other-modules: FusionSpec+ HigherOrderSpec OutputSpec Paths_polysemy hs-source-dirs:
src/Polysemy.hs view
@@ -88,9 +88,6 @@ , reinterpret2H , reinterpret3H - -- * Improving Performance for Interpreters- , inlineRecursiveCalls- -- * Composing IO-based Interpreters , (.@) , (.@@)@@ -119,6 +116,7 @@ , runSemantic , makeSemantic , makeSemantic_+ , inlineRecursiveCalls ) where import Polysemy.Internal
src/Polysemy/Internal.hs view
@@ -15,6 +15,9 @@ , run , runM , raise+ , raiseUnder+ , raiseUnder2+ , raiseUnder3 , Lift (..) , usingSem , liftSem@@ -254,6 +257,45 @@ ------------------------------------------------------------------------------+-- | Like 'raise', but introduces a new effect uunderneath the head of the+-- list.+raiseUnder :: ∀ e2 e1 r a. Sem (e1 ': r) a -> Sem (e1 ': e2 ': r) a+raiseUnder = hoistSem $ hoist raiseUnder_b . weakenUnder+{-# INLINE raiseUnder #-}+++raiseUnder_b :: Sem (e1 ': r) a -> Sem (e1 ': e2 ': r) a+raiseUnder_b = raiseUnder+{-# NOINLINE raiseUnder_b #-}+++------------------------------------------------------------------------------+-- | Like 'raise', but introduces two new effects uunderneath the head of the+-- list.+raiseUnder2 :: ∀ e2 e3 e1 r a. Sem (e1 ': r) a -> Sem (e1 ': e2 ': e3 ': r) a+raiseUnder2 = hoistSem $ hoist raiseUnder2_b . weakenUnder2+{-# INLINE raiseUnder2 #-}+++raiseUnder2_b :: Sem (e1 ': r) a -> Sem (e1 ': e2 ': e3 ': r) a+raiseUnder2_b = raiseUnder2+{-# NOINLINE raiseUnder2_b #-}+++------------------------------------------------------------------------------+-- | Like 'raise', but introduces two new effects uunderneath the head of the+-- list.+raiseUnder3 :: ∀ e2 e3 e4 e1 r a. Sem (e1 ': r) a -> Sem (e1 ': e2 ': e3 ': e4 ': r) a+raiseUnder3 = hoistSem $ hoist raiseUnder3_b . weakenUnder3+{-# INLINE raiseUnder3 #-}+++raiseUnder3_b :: Sem (e1 ': r) a -> Sem (e1 ': e2 ': e3 ': e4 ': r) a+raiseUnder3_b = raiseUnder3+{-# NOINLINE raiseUnder3_b #-}+++------------------------------------------------------------------------------ -- | Lift an effect into a 'Sem'. This is used primarily via -- 'Polysemy.makeSem' to implement smart constructors. send :: Member e r => e (Sem r) a -> Sem r a@@ -311,6 +353,11 @@ -- -- The parentheses here are important; without them you'll run into operator -- precedence errors.+--+-- __Warning:__ This combinator will __duplicate work__ that is intended to be+-- just for initialization. This can result in rather surprising behavior. For+-- a version of '.@' that won't duplicate work, see the @.\@!@ operator in+-- <http://hackage.haskell.org/package/polysemy-zoo/docs/Polysemy-IdempotentLowering.html polysemy-zoo>. (.@) :: Monad m => (∀ x. Sem r x -> m x)@@ -321,7 +368,7 @@ -> Sem (e ': r) z -> m z f .@ g = f . g f-infixl 9 .@+infixl 8 .@ ------------------------------------------------------------------------------@@ -337,5 +384,5 @@ -> Sem (e ': r) z -> m (f z) f .@@ g = f . g f-infixl 9 .@@+infixl 8 .@@
src/Polysemy/Internal/Combinators.hs view
@@ -65,7 +65,7 @@ case decomp u of Left x -> liftSem $ hoist (interpretH_b f) x Right (Yo e s d y) -> do- a <- runTactics s (raise . interpretH_b f . d) (f e)+ a <- runTactics s d (f e) pure $ y a {-# INLINE interpretH #-} @@ -142,7 +142,7 @@ case decompCoerce u of Left x -> k $ hoist (reinterpretH_b f) $ x Right (Yo e s d y) -> do- a <- usingSem k $ runTactics s (raise . reinterpretH_b f . d) $ f e+ a <- usingSem k $ runTactics s (raiseUnder . d) $ f e pure $ y a {-# INLINE[3] reinterpretH #-} -- TODO(sandy): Make this fuse in with 'stateful' directly.@@ -177,7 +177,7 @@ case decompCoerce u of Left x -> k $ weaken $ hoist (reinterpret2H_b f) $ x Right (Yo e s d y) -> do- a <- usingSem k $ runTactics s (raise . reinterpret2H_b f . d) $ f e+ a <- usingSem k $ runTactics s (raiseUnder2 . d) $ f e pure $ y a {-# INLINE[3] reinterpret2H #-} @@ -207,7 +207,7 @@ case decompCoerce u of Left x -> k . weaken . weaken . hoist (reinterpret3H_b f) $ x Right (Yo e s d y) -> do- a <- usingSem k $ runTactics s (raise . reinterpret3H_b f . d) $ f e+ a <- usingSem k $ runTactics s (raiseUnder3 . d) $ f e pure $ y a {-# INLINE[3] reinterpret3H #-}
src/Polysemy/Internal/CustomErrors.hs view
@@ -51,7 +51,8 @@ ':$$: 'Text " add a type application to specify" ':$$: 'Text " " ':<>: PrettyPrint vs- ':<>: 'Text " directly"+ ':<>: 'Text " directly, or activate polysemy-plugin which"+ ':$$: 'Text " can usually infer the type correctly." ) type family PrettyPrint (vs :: [k]) where
src/Polysemy/Internal/TH/Performance.hs view
@@ -1,86 +1,15 @@-{-# LANGUAGE BlockArguments #-}-{-# LANGUAGE TemplateHaskell #-}- {-# OPTIONS_HADDOCK not-home #-} module Polysemy.Internal.TH.Performance ( inlineRecursiveCalls ) where -import Control.Monad-import Data.Bool-import Data.Maybe (maybeToList, mapMaybe)-import Data.Monoid (Any (..))-import Generics.SYB import Language.Haskell.TH ---------------------------------------------------------------------------------- | GHC has a really hard time inlining recursive calls---such as those used in--- interpreters for higher-order effects. This can have disastrous repercussions--- for your performance.------ Fortunately there's a solution, but it's ugly boilerplate. You can enable--- @-XTemplateHaskell@ and use 'inlineRecursiveCalls' to convince GHC to make--- these functions fast again.------ @--- 'inlineRecursiveCalls' [d|--- 'Polysemy.Reader.runReader' :: i -> 'Polysemy.Sem' ('Polysemy.Reader.Reader' i ': r) a -> 'Polysemy.Sem' r a--- 'Polysemy.Reader.runReader' i = 'Polysemy.interpretH' $ \\case--- 'Polysemy.Reader.Ask' -> 'Polysemy.pureT' i--- 'Polysemy.Reader.Local' f m -> do--- mm <- 'Polysemy.runT' m--- 'Polysemy.raise' $ 'Polysemy.Reader.runReader' (f i) mm--- |]--- @-inlineRecursiveCalls :: Q [Dec] -> Q [Dec]-inlineRecursiveCalls m = do- decs <- m- let types = mapMaybe getType decs- inlines = mapMaybe hasInline decs- fmap join $ traverse (loopbreaker types inlines) decs---isRecursive :: Name -> [Clause] -> Bool-isRecursive n cs =- getAny $- everything- (<>)- (mkQ (Any False) $ withRec (const $ Any False) (Any True) n)- cs---withRec :: (Exp -> a) -> a -> Name -> Exp -> a-withRec unmatched matched n = \case- VarE n' | n == n' -> matched- a -> unmatched a---getType :: Dec -> Maybe (Name, Type)-getType (SigD n t) = Just (n, t)-getType _ = Nothing---hasInline :: Dec -> Maybe (Name)-hasInline (PragmaD (InlineP n Inline _ _)) = Just n-hasInline _ = Nothing-+{-# DEPRECATED inlineRecursiveCalls+ "Enabling polysemy-plugin now automatically inlines recursive calls"+ #-} -loopbreaker :: [(Name, Type)] -> [Name] -> Dec -> Q [Dec]-loopbreaker types inlined (FunD n cs)- | isRecursive n cs = do- nLB <- newName $ mconcat- [ "___"- , nameBase n- , "___loop_breaker"- ]- pure $- [ FunD n $ everywhere (mkT $ withRec id (VarE nLB) n) cs- , FunD nLB [Clause [] (NormalB $ VarE n) []]- , PragmaD $ InlineP nLB NoInline FunLike AllPhases- ] ++ maybeToList (fmap (SigD nLB) $ lookup n types)- ++ bool [PragmaD $ InlineP n Inline FunLike AllPhases]- []- (elem n inlined)-loopbreaker _ _ z = pure [z]+inlineRecursiveCalls :: Q [Dec] -> Q [Dec]+inlineRecursiveCalls = id
src/Polysemy/Internal/Union.hs view
@@ -17,6 +17,9 @@ -- * Building Unions , inj , weaken+ , weakenUnder+ , weakenUnder2+ , weakenUnder3 -- * Using Unions , decomp , prj@@ -185,6 +188,27 @@ weaken :: Union r m a -> Union (e ': r) m a weaken (Union n a) = Union (SS n) a {-# INLINE weaken #-}++------------------------------------------------------------------------------+-- | Like 'weaken', but introduces a new effect under the top of the stack.+weakenUnder :: Union (e1 ': r) m a -> Union (e1 ': e2 ': r) m a+weakenUnder (Union SZ a) = Union SZ a+weakenUnder (Union (SS n) a) = Union (SS (SS n)) a+{-# INLINE weakenUnder #-}++------------------------------------------------------------------------------+-- | Like 'weaken', but introduces a new effect under the top of the stack.+weakenUnder2 :: Union (e1 ': r) m a -> Union (e1 ': e2 ': e3 ': r) m a+weakenUnder2 (Union SZ a) = Union SZ a+weakenUnder2 (Union (SS n) a) = Union (SS (SS (SS n))) a+{-# INLINE weakenUnder2 #-}++------------------------------------------------------------------------------+-- | Like 'weaken', but introduces a new effect under the top of the stack.+weakenUnder3 :: Union (e1 ': r) m a -> Union (e1 ': e2 ': e3 ': e4 ': r) m a+weakenUnder3 (Union SZ a) = Union SZ a+weakenUnder3 (Union (SS n) a) = Union (SS (SS (SS (SS n)))) a+{-# INLINE weakenUnder3 #-} ------------------------------------------------------------------------------
+ test/HigherOrderSpec.hs view
@@ -0,0 +1,16 @@+module HigherOrderSpec where++import Polysemy+import Polysemy.Reader+import Test.Hspec+++spec :: Spec+spec = describe "Reader local" $ do+ it "should nest with itself" $ do+ let foo = run . runReader "hello" $ do+ local (++ " world") $ do+ local (++ "!") $ do+ ask+ foo `shouldBe` "hello world!"+