packages feed

freer-simple 1.1.0.0 → 1.2.0.0

raw patch · 7 files changed

+229/−33 lines, 7 filesdep +template-haskelldep ~extensible-effectsPVP ok

version bump matches the API change (PVP)

Dependencies added: template-haskell

Dependency ranges changed: extensible-effects

API changes (from Hackage documentation)

+ Control.Monad.Freer.TH: makeEffect :: Name -> Q [Dec]
+ Control.Monad.Freer.TH: makeEffect_ :: Name -> Q [Dec]
- Control.Monad.Freer: reinterpret2 :: forall f g h effs. (f ~> Eff (g : (h : effs))) -> Eff (f : effs) ~> Eff (g : (h : effs))
+ Control.Monad.Freer: reinterpret2 :: forall f g h effs. (f ~> Eff (g : h : effs)) -> Eff (f : effs) ~> Eff (g : h : effs)
- Control.Monad.Freer: reinterpret3 :: forall f g h i effs. (f ~> Eff (g : (h : (i : effs)))) -> Eff (f : effs) ~> Eff (g : (h : (i : effs)))
+ Control.Monad.Freer: reinterpret3 :: forall f g h i effs. (f ~> Eff (g : h : i : effs)) -> Eff (f : effs) ~> Eff (g : h : i : effs)
- Control.Monad.Freer: type (~>) k (f :: k -> *) (g :: k -> *) = forall (x :: k). () => f x -> g x
+ Control.Monad.Freer: type (~>) (f :: k -> *) (g :: k -> *) = forall (x :: k). () => f x -> g x

Files

CHANGELOG.md view
@@ -1,3 +1,7 @@+# 1.2.0.0 (October 23rd, 2018)++- Added `Control.Monad.Freer.TH`, which provides a `makeEffect` function that automatically generates boilerplate operations using `send` for an effect ([#15](https://github.com/lexi-lambda/freer-simple/pull/15)).+ # 1.1.0.0 (February 20th, 2018)  - Changed the implementation of `LastMember` to avoid an issue similar to the one with `Member` fixed in 1.0.1.1 that could cause the constraint to unnecessarily fail to solve ([#6](https://github.com/lexi-lambda/freer-simple/issues/6)).
bench/Core.hs view
@@ -55,8 +55,8 @@   where go = MTL.get >>= (\n -> if n <= (0 :: Int) then MTL.throwError "wat" else MTL.put (n-1) >> go)  countDownExcEE :: Int -> Either String (Int,Int)-countDownExcEE start = EE.run $ EE.runExc (EE.runState start go)-  where go = EE.get >>= (\n -> if n <= (0 :: Int) then EE.throwExc "wat" else EE.put (n-1) >> go)+countDownExcEE start = EE.run $ EE.runError (EE.runState start go)+  where go = EE.get >>= (\n -> if n <= (0 :: Int) then EE.throwError "wat" else EE.put (n-1) >> go)  --------------------------------------------------------------------------------                           -- Freer: Interpreter --
freer-simple.cabal view
@@ -1,11 +1,11 @@--- This file has been generated from package.yaml by hpack version 0.20.0.+-- This file has been generated from package.yaml by hpack version 0.28.2. -- -- see: https://github.com/sol/hpack ----- hash: f02232dd03e2c26a1697a9b46bb32db8b1f332d4d81f5bb5c309a480589ea5d1+-- hash: 4f06465ab3fa36c72dff4e9197bc984c95dc787f823d64bf28f26b1a73a33a54  name:           freer-simple-version:        1.1.0.0+version:        1.2.0.0 synopsis:       Implementation of a friendly effect system for Haskell. description:    An implementation of an effect system for Haskell (a fork of                 <http://hackage.haskell.org/package/freer-effects freer-effects>), which is@@ -30,7 +30,6 @@ license-file:   LICENSE build-type:     Simple cabal-version:  >= 1.10- extra-source-files:     CHANGELOG.md     README.md@@ -40,14 +39,6 @@   location: https://github.com/lexi-lambda/freer-simple  library-  hs-source-dirs:-      src-  default-extensions: ConstraintKinds DataKinds DeriveFunctor FlexibleContexts FlexibleInstances FunctionalDependencies GADTs LambdaCase MultiParamTypeClasses RankNTypes ScopedTypeVariables TypeApplications TypeOperators-  ghc-options: -Wall -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wredundant-constraints-  build-depends:-      base >=4.9 && <5-    , natural-transformation >=0.2-    , transformers-base   exposed-modules:       Control.Monad.Freer       Control.Monad.Freer.Coroutine@@ -57,6 +48,7 @@       Control.Monad.Freer.NonDet       Control.Monad.Freer.Reader       Control.Monad.Freer.State+      Control.Monad.Freer.TH       Control.Monad.Freer.Trace       Control.Monad.Freer.Writer       Data.FTCQueue@@ -64,17 +56,19 @@       Data.OpenUnion.Internal   other-modules:       Paths_freer_simple-  default-language: Haskell2010--executable freer-examples-  main-is: Main.hs   hs-source-dirs:-      examples/src+      src   default-extensions: ConstraintKinds DataKinds DeriveFunctor FlexibleContexts FlexibleInstances FunctionalDependencies GADTs LambdaCase MultiParamTypeClasses RankNTypes ScopedTypeVariables TypeApplications TypeOperators   ghc-options: -Wall -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wredundant-constraints   build-depends:       base >=4.9 && <5-    , freer-simple+    , natural-transformation >=0.2+    , template-haskell >=2.11 && <2.15+    , transformers-base+  default-language: Haskell2010++executable freer-examples+  main-is: Main.hs   other-modules:       Capitalize       Console@@ -82,11 +76,28 @@       Fresh       Trace       Paths_freer_simple+  hs-source-dirs:+      examples/src+  default-extensions: ConstraintKinds DataKinds DeriveFunctor FlexibleContexts FlexibleInstances FunctionalDependencies GADTs LambdaCase MultiParamTypeClasses RankNTypes ScopedTypeVariables TypeApplications TypeOperators+  ghc-options: -Wall -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wredundant-constraints+  build-depends:+      base >=4.9 && <5+    , freer-simple   default-language: Haskell2010  test-suite unit   type: exitcode-stdio-1.0   main-is: Tests.hs+  other-modules:+      Tests.Coroutine+      Tests.Exception+      Tests.Fresh+      Tests.Loop+      Tests.NonDet+      Tests.Reader+      Tests.State+      Tests.TH+      Paths_freer_simple   hs-source-dirs:       tests   default-extensions: ConstraintKinds DataKinds DeriveFunctor FlexibleContexts FlexibleInstances FunctionalDependencies GADTs LambdaCase MultiParamTypeClasses RankNTypes ScopedTypeVariables TypeApplications TypeOperators@@ -98,20 +109,13 @@     , tasty     , tasty-hunit     , tasty-quickcheck-  other-modules:-      Tests.Coroutine-      Tests.Exception-      Tests.Fresh-      Tests.Loop-      Tests.NonDet-      Tests.Reader-      Tests.State-      Paths_freer_simple   default-language: Haskell2010  benchmark core   type: exitcode-stdio-1.0   main-is: Core.hs+  other-modules:+      Paths_freer_simple   hs-source-dirs:       bench   default-extensions: ConstraintKinds DataKinds DeriveFunctor FlexibleContexts FlexibleInstances FunctionalDependencies GADTs LambdaCase MultiParamTypeClasses RankNTypes ScopedTypeVariables TypeApplications TypeOperators@@ -119,10 +123,8 @@   build-depends:       base >=4.9 && <5     , criterion-    , extensible-effects <2+    , extensible-effects     , free     , freer-simple     , mtl-  other-modules:-      Paths_freer_simple   default-language: Haskell2010
src/Control/Monad/Freer.hs view
@@ -91,7 +91,7 @@       'Just' contents -> 'pure' contents       'Nothing' -> 'error' ("readFile: no such file " ++ path)     WriteFile path contents -> 'Control.Monad.Freer.State.modify' $ \\vfs ->-      (path, contents) : 'Data.List.delete' (path, contents) vfs+      (path, contents) : 'Data.List.deleteBy' (('==') ``Data.Function.on`` 'fst') (path, contents) vfs @  This handler is easy to write, doesn’t require any knowledge of how@@ -161,6 +161,10 @@  …then 'readFile' would /only/ be usable with an 'Eff' computation that /only/ performed @FileSystem@ effects, which isn’t especially useful.++Since writing these functions is entirely mechanical, they can be generated+automatically using Template Haskell; see "Control.Monad.Freer.TH" for more+details. -} module Control.Monad.Freer   ( -- * Effect Monad
+ src/Control/Monad/Freer/TH.hs view
@@ -0,0 +1,150 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TemplateHaskell #-}++-- Originally ported from code written by Sandy Maguire (@isovector), available+-- at https://github.com/IxpertaSolutions/freer-effects/pull/28.++{-|+This module provides Template Haskell functions for automatically generating+effect operation functions (that is, functions that use 'send') from a given+effect algebra. For example, using the @FileSystem@ effect from the example in+the module documentation for "Control.Monad.Freer", we can write the following:++@+data FileSystem r where+  ReadFile :: 'FilePath' -> FileSystem 'String'+  WriteFile :: 'FilePath' -> 'String' -> FileSystem ()+'makeEffect' ''FileSystem+@++This will automatically generate the following functions:++@+readFile :: 'Member' FileSystem effs => 'FilePath' -> 'Eff' effs 'String'+readFile a = 'send' (ReadFile a)++writeFile :: 'Member' FileSystem effs => 'FilePath' -> 'String' -> 'Eff' effs ()+writeFile a b = 'send' (WriteFile a b)+@+-}+module Control.Monad.Freer.TH+  ( makeEffect+  , makeEffect_+  )+where++import Control.Monad (forM, unless)+import Control.Monad.Freer (send, Member, Eff)+import Data.Char (toLower)+import Data.List (nub)+import Data.Maybe (mapMaybe)+import Language.Haskell.TH+import Prelude+++-- | If @T@ is a GADT representing an effect algebra, as described in the module+-- documentation for "Control.Monad.Freer", @$('makeEffect' ''T)@ automatically+-- generates a function that uses 'send' with each operation. For more+-- information, see the module documentation for "Control.Monad.Freer.TH".+makeEffect :: Name -> Q [Dec]+makeEffect = genFreer True++-- | Like 'makeEffect', but does not provide type signatures. This can be used+-- to attach Haddock comments to individual arguments for each generated+-- function.+--+-- @+-- data Lang x where+--   Output :: String -> Lang ()+--+-- makeEffect_ ''Lang+--+-- -- | Output a string.+-- output :: Member Lang effs+--        => String    -- ^ String to output.+--        -> Eff effs ()  -- ^ No result.+-- @+--+-- Note that 'makeEffect_' must be used /before/ the explicit type signatures.+makeEffect_ :: Name -> Q [Dec]+makeEffect_ = genFreer False++-- | Generates declarations and possibly signatures for functions to lift GADT+-- constructors into 'Eff' actions.+genFreer :: Bool -> Name -> Q [Dec]+genFreer makeSigs tcName = do+  -- The signatures for the generated definitions require FlexibleContexts.+  isExtEnabled FlexibleContexts+    >>= flip unless (fail "makeEffect requires FlexibleContexts to be enabled")++  reify tcName >>= \case+    TyConI (DataD _ _ _ _ cons _) -> do+      sigs <- filter (const makeSigs) <$> mapM genSig cons+      decs <- mapM genDecl cons+      return $ sigs ++ decs++    _ -> fail "makeEffect expects a type constructor"++-- | Given the name of a GADT constructor, return the name of the corresponding+-- lifted function.+getDeclName :: Name -> Name+getDeclName = mkName . overFirst toLower . nameBase+ where+  overFirst f (a : as) = f a : as+  overFirst _ as       = as++-- | Builds a function definition of the form @x a b c = send $ X a b c@.+genDecl :: Con -> Q Dec+genDecl (ForallC _       _     con) = genDecl con+genDecl (GadtC   [cName] tArgs _  ) = do+  let fnName = getDeclName cName+  let arity  = length tArgs - 1+  dTypeVars <- forM [0 .. arity] $ const $ newName "a"+  return $ FunD fnName . pure $ Clause+    (VarP <$> dTypeVars)+    (NormalB . AppE (VarE 'send) $ foldl+      (\b -> AppE b . VarE)+      (ConE cName)+      dTypeVars+    )+    []+genDecl _ = fail "genDecl expects a GADT constructor"++-- | Generates a type signature of the form+-- @x :: Member (Effect e) effs => a -> b -> c -> Eff effs r@.+genSig :: Con -> Q Dec+genSig (ForallC _       _      con                    ) = genSig con+genSig (GadtC   [cName] tArgs' ctrType@(AppT eff tRet)) = do+  effs <- newName "effs"+  let+    fnName    = getDeclName cName+    tArgs     = fmap snd tArgs'+    otherVars = unapply ctrType+    quantifiedVars =+      fmap PlainTV . nub $ effs : mapMaybe freeVarName (tArgs ++ otherVars)+    memberConstraint = ConT ''Member `AppT` eff `AppT` VarT effs+    resultType       = ConT ''Eff `AppT` VarT effs `AppT` tRet++  return+    .  SigD fnName+    .  ForallT quantifiedVars [memberConstraint]+    .  foldArrows+    $  tArgs+    ++ [resultType]+-- TODO: Although this should never happen, we obviously need a better error message below.+genSig GadtC{} = fail "genSig can only look at applications (AppT)"+genSig _       = fail "genSig expects a GADT constructor"++-- | Gets the name of the free variable in the 'Type', if it exists.+freeVarName :: Type -> Maybe Name+freeVarName (VarT n) = Just n+freeVarName _        = Nothing++-- | Folds a list of 'Type's into a right-associative arrow 'Type'.+foldArrows :: [Type] -> Type+foldArrows = foldr1 (AppT . AppT ArrowT)++-- | Unfolds a type into any types which were applied together.+unapply :: Type -> [Type]+unapply (AppT a b) = unapply a ++ unapply b+unapply a          = [a]
tests/Tests.hs view
@@ -12,6 +12,7 @@ import qualified Tests.Reader (tests) import qualified Tests.State (tests) import qualified Tests.Loop (tests)+import qualified Tests.TH (tests)  --------------------------------------------------------------------------------                            -- Pure Tests --@@ -38,4 +39,5 @@   , Tests.Reader.tests   , Tests.State.tests   , Tests.Loop.tests+  , Tests.TH.tests   ]
+ tests/Tests/TH.hs view
@@ -0,0 +1,34 @@+{-# LANGUAGE TemplateHaskell #-}+module Tests.TH where++import Test.Tasty (TestTree, testGroup)+import Test.Tasty.QuickCheck (testProperty)++import Control.Monad.Freer (Eff, run, interpret, type(~>))+import Control.Monad.Freer.TH (makeEffect)++-- Create a test GADT for our effects.+data Prepender next where+  PrependSomething :: String -> Prepender String++-- Make TH generate our effect functions.+makeEffect ''Prepender++tests :: TestTree+tests = testGroup+  "TH tests"+  [ testProperty "Prepender uses generated effects"+      $ \s -> testGeneratedFunction s == ("prepended: " ++ s)+  ]++--------------------------------------------------------------------------------+                            -- Examples --+--------------------------------------------------------------------------------+runPrepender :: Eff (Prepender ': effs) ~> Eff effs+runPrepender = interpret+  (\case+  PrependSomething s -> pure $ "prepended: " ++ s+  )++testGeneratedFunction :: String -> String+testGeneratedFunction s = run . runPrepender $ prependSomething s