on master help sign in
Experimental arrow-based approach
commit6268e4a4fe961ca810da1ecb2275142a301f0813 authorJasper Van der Jeugt <jaspervdj@gmail.com> dateWed 29 Dec 2010, 22:59 (16 years ago) parentbf31c55
7 files changed+98−159
src/Hakyll/Core/Compiler.hs+69−35 src/Hakyll/Core/Rules.hs+6−6 src/Hakyll/Core/Run.hs+14−16 src/Hakyll/Core/Target.hs−31 src/Hakyll/Core/Target/Internal.hs−66 src/Hakyll/Web/FileType.hs+4−4 src/Hakyll/Web/Pandoc.hs+5−1
src/Hakyll/Core/Compiler.hs+69−35
@@ -4,26 +4,32 @@
44module Hakyll.Core.Compiler
55 ( Dependencies
66 , CompilerM
7− , Compiler
7+ , Compiler (..)
88 , runCompiler
9+ , getIdentifier
10+ , getResourceString
911 , require
10− , requireAll
11− , compileFromString
12+ -- , requireAll
13+ -- , compileFromString
1214 ) where
1315
14−import Control.Arrow (second)
16+import Prelude hiding ((.), id)
17+import Control.Arrow (second, (>>>))
1518import Control.Applicative (Applicative, (<$>))
1619import Control.Monad.State (State, modify, runState)
1720import Control.Monad.Reader (ReaderT, ask, runReaderT)
21+import Control.Monad.Trans (liftIO)
22+import Control.Monad ((<=<))
1823import Data.Set (Set)
1924import qualified Data.Set as S
20−import Data.Typeable (Typeable)
25+import Control.Category (Category, (.), id)
26+import Control.Arrow (Arrow, arr, first)
27+
2128import Data.Binary (Binary)
29+import Data.Typeable (Typeable)
2230
2331import Hakyll.Core.Identifier
2432import Hakyll.Core.Identifier.Pattern
25−import Hakyll.Core.Target
26−import Hakyll.Core.Target.Internal
2733import Hakyll.Core.CompiledItem
2834import Hakyll.Core.Writable
2935import Hakyll.Core.ResourceProvider
@@ -32,65 +38,92 @@ import Hakyll.Core.ResourceProvider
3238--
3339type Dependencies = Set Identifier
3440
35−-- | Add one dependency
41+-- | A lookup with which we can get dependencies
3642--
37−addDependency :: Identifier -> CompilerM ()
38−addDependency dependency = CompilerM $ modify $ addDependency'
39− where
40− addDependency' x = x
41− { compilerDependencies = S.insert dependency $ compilerDependencies x
42− }
43+type DependencyLookup = Identifier -> CompiledItem
4344
4445-- | Environment in which a compiler runs
4546--
4647data CompilerEnvironment = CompilerEnvironment
4748 { compilerIdentifier :: Identifier -- ^ Target identifier
4849 , compilerResourceProvider :: ResourceProvider -- ^ Resource provider
49− }
50−
51−-- | State carried along by a compiler
52−--
53−data CompilerState = CompilerState
54− { compilerDependencies :: Dependencies
50+ , compilerDependencyLookup :: DependencyLookup -- ^ Dependency lookup
5551 }
5652
5753-- | The compiler monad
5854--
5955newtype CompilerM a = CompilerM
60− { unCompilerM :: ReaderT CompilerEnvironment (State CompilerState) a
56+ { unCompilerM :: ReaderT CompilerEnvironment IO a
6157 } deriving (Monad, Functor, Applicative)
6258
63−-- | Simplified type for a compiler generating a target (which covers most
64−-- cases)
59+-- | The compiler arrow
6560--
66−type Compiler a = CompilerM (TargetM a)
61+data Compiler a b = Compiler
62+ { -- TODO: Reader ResourceProvider Dependencies
63+ compilerDependencies :: Dependencies
64+ , compilerJob :: a -> CompilerM b
65+ }
66+
67+instance Category Compiler where
68+ id = Compiler S.empty return
69+ (Compiler d1 j1) . (Compiler d2 j2) =
70+ Compiler (d1 `S.union` d2) (j1 <=< j2)
71+
72+instance Arrow Compiler where
73+ arr f = Compiler S.empty (return . f)
74+ first (Compiler d j) = Compiler d $ \(x, y) -> do
75+ x' <- j x
76+ return (x', y)
6777
6878-- | Run a compiler, yielding the resulting target and it's dependencies
6979--
70−runCompiler :: Compiler a -> Identifier -> ResourceProvider
71− -> (TargetM a, Dependencies)
72−runCompiler compiler identifier provider = second compilerDependencies $
73− runState (runReaderT (unCompilerM compiler) env) state
80+runCompiler :: Compiler () a
81+ -> Identifier
82+ -> ResourceProvider
83+ -> DependencyLookup
84+ -> IO a
85+runCompiler compiler identifier provider lookup' =
86+ runReaderT (unCompilerM $ compilerJob compiler ()) env
7487 where
75− state = CompilerState S.empty
7688 env = CompilerEnvironment
7789 { compilerIdentifier = identifier
7890 , compilerResourceProvider = provider
91+ , compilerDependencyLookup = lookup'
7992 }
8093
94+addDependency :: Identifier
95+ -> Compiler b b
96+addDependency id' = Compiler (S.singleton id') return
97+
98+fromCompilerM :: (a -> CompilerM b)
99+ -> Compiler a b
100+fromCompilerM = Compiler S.empty
101+
102+getIdentifier :: Compiler () Identifier
103+getIdentifier = fromCompilerM $ const $ CompilerM $
104+ compilerIdentifier <$> ask
105+
106+getResourceString :: Compiler () String
107+getResourceString = getIdentifier >>> getResourceString'
108+ where
109+ getResourceString' = fromCompilerM $ \id' -> CompilerM $ do
110+ provider <- compilerResourceProvider <$> ask
111+ liftIO $ resourceString provider id'
81112
82113-- | Require another target. Using this function ensures automatic handling of
83114-- dependencies
84115--
85116require :: (Binary a, Typeable a, Writable a)
86117 => Identifier
87− -> Compiler a
88−require identifier = do
89− addDependency identifier
90− return $ TargetM $ do
91− lookup' <- targetDependencyLookup <$> ask
92− return $ unCompiledItem $ lookup' identifier
118+ -> (a -> b -> c)
119+ -> Compiler b c
120+require identifier f = addDependency identifier >>> fromCompilerM require'
121+ where
122+ require' x = CompilerM $ do
123+ lookup' <- compilerDependencyLookup <$> ask
124+ return $ f (unCompiledItem $ lookup' identifier) x
93125
126+{-
94127-- | Require a number of targets. Using this function ensures automatic handling
95128-- of dependencies
96129--
@@ -108,3 +141,4 @@ requireAll pattern = CompilerM $ do
108141compileFromString :: (String -> TargetM a) -- ^ Function to create the target
109142 -> Compiler a -- ^ Resulting compiler
110143compileFromString = return . (getResourceString >>=)
144+-}
src/Hakyll/Core/Rules.hs+6−6
@@ -15,7 +15,7 @@ module Hakyll.Core.Rules
1515import Control.Applicative (Applicative, (<$>))
1616import Control.Monad.Writer
1717import Control.Monad.Reader
18−import Control.Arrow (second)
18+import Control.Arrow (second, (>>>), arr)
1919
2020import Data.Typeable (Typeable)
2121import Data.Binary (Binary)
@@ -32,7 +32,7 @@ import Hakyll.Core.Writable
3232--
3333data RuleSet = RuleSet
3434 { rulesRoute :: Route
35− , rulesCompilers :: [(Identifier, Compiler CompiledItem)]
35+ , rulesCompilers :: [(Identifier, Compiler () CompiledItem)]
3636 }
3737
3838instance Monoid RuleSet where
@@ -64,12 +64,12 @@ addRoute route' = RulesM $ tell $ RuleSet route' mempty
6464-- | Add a number of compilers
6565--
6666addCompilers :: (Binary a, Typeable a, Writable a)
67− => [(Identifier, Compiler a)]
67+ => [(Identifier, Compiler () a)]
6868 -> Rules
6969addCompilers compilers = RulesM $ tell $ RuleSet mempty $
7070 map (second boxCompiler) compilers
7171 where
72− boxCompiler = fmap (fmap compiledItem)
72+ boxCompiler = (>>> arr compiledItem)
7373
7474-- | Add a compilation rule
7575--
@@ -77,7 +77,7 @@ addCompilers compilers = RulesM $ tell $ RuleSet mempty $
7777-- the given compiler
7878--
7979compile :: (Binary a, Typeable a, Writable a)
80− => Pattern -> Compiler a -> Rules
80+ => Pattern -> Compiler () a -> Rules
8181compile pattern compiler = RulesM $ do
8282 identifiers <- matches pattern . resourceList <$> ask
8383 unRulesM $ addCompilers $ zip identifiers (repeat compiler)
@@ -87,7 +87,7 @@ compile pattern compiler = RulesM $ do
8787-- This sets a compiler for the given identifier
8888--
8989create :: (Binary a, Typeable a, Writable a)
90− => Identifier -> Compiler a -> Rules
90+ => Identifier -> Compiler () a -> Rules
9191create identifier compiler = addCompilers [(identifier, compiler)]
9292
9393-- | Add a route
src/Hakyll/Core/Run.hs+14−16
@@ -11,12 +11,12 @@ import Data.Binary (Binary)
1111import System.FilePath ((</>))
1212
1313import Hakyll.Core.Route
14+import Hakyll.Core.Identifier
1415import Hakyll.Core.Util.File
1516import Hakyll.Core.Compiler
1617import Hakyll.Core.ResourceProvider
1718import Hakyll.Core.ResourceProvider.FileResourceProvider
1819import Hakyll.Core.Rules
19−import Hakyll.Core.Target
2020import Hakyll.Core.DirectedGraph
2121import Hakyll.Core.DirectedGraph.Dot
2222import Hakyll.Core.DirectedGraph.DependencySolver
@@ -38,22 +38,20 @@ hakyllWith rules provider store = do
3838 -- Get all identifiers and compilers
3939 compilers = rulesCompilers ruleSet
4040
41− -- Get all targets
42− targets = flip map compilers $ \(id', compiler) ->
43− let (targ, deps) = runCompiler compiler id' provider
44− in (id', targ, deps)
41+ -- Get all dependencies
42+ dependencies = flip map compilers $ \(id', compiler) ->
43+ let deps = compilerDependencies compiler
44+ in (id', deps)
4545
46− -- Map mapping every identifier to it's target
47− targetMap = M.fromList $ map (\(i, t, _) -> (i, t)) targets
46+ -- Create a compiler map
47+ compilerMap = M.fromList compilers
4848
49− -- Create a dependency graph
50− graph = fromList $ map (\(i, _, d) -> (i, d)) targets
51−
52− -- Solve the graph, creating a target order
49+ -- Create and solve the graph, creating a compiler order
50+ graph = fromList dependencies
5351 ordered = solveDependencies graph
5452
55− -- Join the order with the targets again
56− orderedTargets = map (id &&& (targetMap M.!)) ordered
53+ -- Join the order with the compilers again
54+ orderedCompilers = map (id &&& (compilerMap M.!)) ordered
5755
5856 -- Fetch the routes
5957 route' = rulesRoute ruleSet
@@ -62,12 +60,12 @@ hakyllWith rules provider store = do
6260 writeDot "dependencies.dot" show graph
6361
6462 -- Generate all the targets in order
65− _ <- foldM (addTarget route') M.empty orderedTargets
63+ _ <- foldM (addTarget route') M.empty orderedCompilers
6664
6765 putStrLn "DONE."
6866 where
69− addTarget route' map' (id', targ) = do
70− compiled <- runTarget targ id' (dependencyLookup map') provider store
67+ addTarget route' map' (id', comp) = do
68+ compiled <- runCompiler comp id' provider (dependencyLookup map')
7169 putStrLn $ "Generated target: " ++ show id'
7270
7371 case runRoute route' id' of
src/Hakyll/Core/Target.hsdeleted−31
@@ -1,31 +0,0 @@
1−-- | A target represents one compilation unit, e.g. a blog post, a CSS file...
2−--
3−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
4−module Hakyll.Core.Target
5− ( DependencyLookup
6− , TargetM
7− , runTarget
8− , getIdentifier
9− , getResourceString
10− ) where
11−
12−import Control.Applicative ((<$>))
13−import Control.Monad.Reader (ask)
14−import Control.Monad.Trans (liftIO)
15−
16−import Hakyll.Core.Identifier
17−import Hakyll.Core.Target.Internal
18−import Hakyll.Core.ResourceProvider
19−
20−-- | Get the current identifier
21−--
22−getIdentifier :: TargetM Identifier
23−getIdentifier = TargetM $ targetIdentifier <$> ask
24−
25−-- | Get the resource content as a string
26−--
27−getResourceString :: TargetM String
28−getResourceString = TargetM $ do
29− provider <- targetResourceProvider <$> ask
30− identifier <- unTargetM getIdentifier
31− liftIO $ resourceString provider identifier
src/Hakyll/Core/Target/Internal.hsdeleted−66
@@ -1,66 +0,0 @@
1−-- | Internal structure of a Target, not exported outside of the library
2−--
3−{-# LANGUAGE GeneralizedNewtypeDeriving, Rank2Types #-}
4−module Hakyll.Core.Target.Internal
5− ( DependencyLookup
6− , TargetEnvironment (..)
7− , TargetM (..)
8− , runTarget
9− ) where
10−
11−import Control.Applicative (Applicative)
12−import Control.Monad.Trans (MonadIO)
13−import Control.Monad.Reader (ReaderT, runReaderT)
14−import Control.Monad.State (StateT, evalStateT)
15−
16−import Hakyll.Core.Identifier
17−import Hakyll.Core.ResourceProvider
18−import Hakyll.Core.Store
19−import Hakyll.Core.CompiledItem
20−
21−-- | A lookup with which we can get dependencies
22−--
23−type DependencyLookup = Identifier -> CompiledItem
24−
25−-- | Environment for the target monad
26−--
27−data TargetEnvironment = TargetEnvironment
28− { targetIdentifier :: Identifier -- ^ Identifier
29− , targetDependencyLookup :: DependencyLookup -- ^ Dependency lookup
30− , targetResourceProvider :: ResourceProvider -- ^ To get resources
31− , targetStore :: Store -- ^ Store for caching
32− }
33−
34−-- | State for the target monad
35−--
36−data TargetState = TargetState
37− { targetSnapshot :: Int -- ^ Snapshot ID
38− }
39−
40−-- | Monad for targets. In this monad, the user can compose targets and describe
41−-- how they should be created.
42−--
43−newtype TargetM a = TargetM
44− { unTargetM :: ReaderT TargetEnvironment (StateT TargetState IO) a
45− } deriving (Monad, Functor, Applicative, MonadIO)
46−
47−-- | Run a target, yielding an actual result.
48−--
49−runTarget :: TargetM a
50− -> Identifier
51− -> DependencyLookup
52− -> ResourceProvider
53− -> Store
54− -> IO a
55−runTarget target id' lookup' provider store =
56− evalStateT (runReaderT (unTargetM target) env) state
57− where
58− env = TargetEnvironment
59− { targetIdentifier = id'
60− , targetDependencyLookup = lookup'
61− , targetResourceProvider = provider
62− , targetStore = store
63− }
64− state = TargetState
65− { targetSnapshot = 0
66− }
src/Hakyll/Web/FileType.hs+4−4
@@ -7,10 +7,10 @@ module Hakyll.Web.FileType
77 ) where
88
99import System.FilePath (takeExtension)
10−import Control.Applicative ((<$>))
10+import Control.Arrow ((>>>), arr)
1111
1212import Hakyll.Core.Identifier
13−import Hakyll.Core.Target
13+import Hakyll.Core.Compiler
1414
1515-- | Datatype to represent the different file types Hakyll can deal with by
1616-- default
@@ -51,5 +51,5 @@ fileType = fileType' . takeExtension
5151
5252-- | Get the file type for the current file
5353--
54−getFileType :: TargetM FileType
55−getFileType = fileType . toFilePath <$> getIdentifier
54+getFileType :: Compiler () FileType
55+getFileType = getIdentifier >>> arr (fileType . toFilePath)
src/Hakyll/Web/Pandoc.hs+5−1
@@ -8,10 +8,12 @@ module Hakyll.Web.Pandoc
88 , writePandocWith
99
1010 -- * Functions working on pages/targets
11+ {-
1112 , pageReadPandoc
1213 , pageReadPandocWith
1314 , pageRenderPandoc
1415 , pageRenderPandocWith
16+ -}
1517
1618 -- * Default options
1719 , defaultParserState
@@ -23,7 +25,7 @@ import Control.Applicative ((<$>))
2325import Text.Pandoc (Pandoc)
2426import qualified Text.Pandoc as P
2527
26−import Hakyll.Core.Target
28+import Hakyll.Core.Compiler
2729import Hakyll.Web.FileType
2830import Hakyll.Web.Page
2931
@@ -62,6 +64,7 @@ writePandocWith :: P.WriterOptions -- ^ Writer options for pandoc
6264 -> String -- ^ Resulting HTML
6365writePandocWith = P.writeHtmlString
6466
67+{-
6568-- | Read the resource using pandoc
6669--
6770pageReadPandoc :: Page String -> TargetM (Page Pandoc)
@@ -88,6 +91,7 @@ pageRenderPandocWith :: P.ParserState
8891pageRenderPandocWith state options page = do
8992 pandoc <- pageReadPandocWith state page
9093 return $ writePandocWith options <$> pandoc
94+-}
9195
9296-- | The default reader options for pandoc parsing in hakyll
9397--