diff options
| -rw-r--r-- | src/Main.hs | 6 | ||||
| -rw-r--r-- | src/Sequence/Formula.hs | 38 | ||||
| -rw-r--r-- | src/Sequence/Types.hs | 6 |
3 files changed, 37 insertions, 13 deletions
diff --git a/src/Main.hs b/src/Main.hs index 06cc6ed..f4a863f 100644 --- a/src/Main.hs +++ b/src/Main.hs | |||
| @@ -2,7 +2,7 @@ | |||
| 2 | 2 | ||
| 3 | import Control.Monad | 3 | import Control.Monad |
| 4 | 4 | ||
| 5 | import Control.Lens | 5 | import Control.Lens hiding (Context(..)) |
| 6 | 6 | ||
| 7 | import System.Console.Shell | 7 | import System.Console.Shell |
| 8 | import System.Console.Shell.ShellMonad | 8 | import System.Console.Shell.ShellMonad |
| @@ -146,7 +146,7 @@ rollTest = withArg $ enactTest' >=> maybe (return ()) (shellPutStrLn . ppResult) | |||
| 146 | 146 | ||
| 147 | enactTest' :: (FormulaM Stats Test) -> Sh GameState (Maybe TestResult) | 147 | enactTest' :: (FormulaM Stats Test) -> Sh GameState (Maybe TestResult) |
| 148 | enactTest' test = withFocus' $ \focus -> do | 148 | enactTest' test = withFocus' $ \focus -> do |
| 149 | (newStats, result) <- evalFormula (view eStats focus) (enactTest =<< test) | 149 | (newFocus, result) <- evalFormula focus (enactTest =<< test) |
| 150 | gFocus'.eStats .= newStats | 150 | gFocus' .= newFocus |
| 151 | return result | 151 | return result |
| 152 | 152 | ||
diff --git a/src/Sequence/Formula.hs b/src/Sequence/Formula.hs index 0d99fc0..9000c58 100644 --- a/src/Sequence/Formula.hs +++ b/src/Sequence/Formula.hs | |||
| @@ -1,13 +1,14 @@ | |||
| 1 | {-# LANGUAGE RecordWildCards, RankNTypes, TypeSynonymInstances, FlexibleInstances, MultiParamTypeClasses, ViewPatterns, TypeFamilies #-} | 1 | {-# LANGUAGE RecordWildCards, RankNTypes, TypeSynonymInstances, FlexibleInstances, MultiParamTypeClasses, ViewPatterns, TypeFamilies, GADTs, TypeOperators, ExistentialQuantification, FlexibleContexts #-} |
| 2 | 2 | ||
| 3 | module Sequence.Formula | 3 | module Sequence.Formula |
| 4 | ( FormulaM, Formula, quot' | 4 | ( FormulaM, Formula, quot' |
| 5 | , (:<:)(..), Context(..) | ||
| 5 | , evalFormula | 6 | , evalFormula |
| 6 | , val | 7 | , val |
| 7 | , d, z | 8 | , d, z |
| 8 | ) where | 9 | ) where |
| 9 | 10 | ||
| 10 | import Control.Lens | 11 | import Control.Lens hiding (Context(..)) |
| 11 | import Data.Data.Lens | 12 | import Data.Data.Lens |
| 12 | 13 | ||
| 13 | import Control.Monad | 14 | import Control.Monad |
| @@ -30,8 +31,27 @@ import Data.Either | |||
| 30 | import Data.Set (Set) | 31 | import Data.Set (Set) |
| 31 | import qualified Data.Set as Set | 32 | import qualified Data.Set as Set |
| 32 | 33 | ||
| 34 | class (:<:) small large where | ||
| 35 | ctx' :: Traversal' large small | ||
| 33 | 36 | ||
| 34 | type FormulaM input a = StateT (Set String) (ReaderT input (ExceptT (Question input) EventM)) a | 37 | instance a :<: a where |
| 38 | ctx' = simple | ||
| 39 | |||
| 40 | instance a :<: (a, a) where | ||
| 41 | ctx' = both | ||
| 42 | |||
| 43 | instance a :<: (a, b) where | ||
| 44 | ctx' = _1 | ||
| 45 | |||
| 46 | instance a :<: (b, a) where | ||
| 47 | ctx' = _2 | ||
| 48 | |||
| 49 | data Context small = forall large. (small :<: large) => Context large | ||
| 50 | |||
| 51 | ctx :: Traversal' (Context input) input | ||
| 52 | ctx modifySmall (Context large) = Context <$> ctx' modifySmall large | ||
| 53 | |||
| 54 | type FormulaM input a = StateT (Set String) (ReaderT (Context input) (ExceptT (Question input) EventM)) a | ||
| 35 | 55 | ||
| 36 | type Formula input = FormulaM input Int | 56 | type Formula input = FormulaM input Int |
| 37 | 57 | ||
| @@ -66,22 +86,22 @@ instance Integral a => Num (FormulaM input a) where | |||
| 66 | quot' :: Integral a => FormulaM input a -> FormulaM input a -> FormulaM input a | 86 | quot' :: Integral a => FormulaM input a -> FormulaM input a -> FormulaM input a |
| 67 | quot' = liftM2 quot | 87 | quot' = liftM2 quot |
| 68 | 88 | ||
| 69 | askQuestion :: MonadIO m => input -> (Question input) -> m input | 89 | askQuestion :: (MonadIO m, sInput :<: lInput) => lInput -> Question sInput -> m lInput |
| 70 | askQuestion input q@(Question{..}) = flip (set answer) input . maybe (throwError q) return <$> askQ prompt (join . fmap readMaybe) | 90 | askQuestion input q@(Question{..}) = flip (set $ ctx' . answer) input . maybe (throwError q) return <$> askQ prompt (join . fmap readMaybe) |
| 71 | 91 | ||
| 72 | evalFormula :: (MonadIO m, Ord a, Show a) => input -> FormulaM input a -> m (input, a) | 92 | evalFormula :: (MonadIO m, sInput :<: lInput) => lInput -> FormulaM sInput a -> m (lInput, a) |
| 73 | evalFormula = evalFormula' [] | 93 | evalFormula = evalFormula' [] |
| 74 | where | 94 | where |
| 75 | evalFormula' finalChanges input formula = do | 95 | evalFormula' finalChanges input formula = do |
| 76 | result <- liftIO . enact . runExceptT . (runReaderT ?? input) . (evalStateT ?? Set.empty) $ formula | 96 | result <- liftIO . enact . runExceptT . (runReaderT ?? (Context input)) . (evalStateT ?? Set.empty) $ formula |
| 77 | case result of | 97 | case result of |
| 78 | Left q@(Question{..}) -> askQuestion input q >>= flip (evalFormula' $ bool (pure . set answer $ throwError q) mempty keepResult ++ finalChanges) formula | 98 | Left q@(Question{..}) -> askQuestion input q >>= flip (evalFormula' $ bool (pure . set (ctx' . answer) $ throwError q) mempty keepResult ++ finalChanges) formula |
| 79 | Right result -> return (foldr ($) input finalChanges, result) | 99 | Right result -> return (foldr ($) input finalChanges, result) |
| 80 | 100 | ||
| 81 | val :: Integral a => Traversal' input (Formula input) -> String -> Bool -> Formula input | 101 | val :: Integral a => Traversal' input (Formula input) -> String -> Bool -> Formula input |
| 82 | val answer prompt keepResult = do | 102 | val answer prompt keepResult = do |
| 83 | gets (Set.member prompt) >>= bool (modify $ Set.insert prompt) (modify (Set.delete prompt) >> throwError Question{..}) | 103 | gets (Set.member prompt) >>= bool (modify $ Set.insert prompt) (modify (Set.delete prompt) >> throwError Question{..}) |
| 84 | preview answer >>= maybe (throwError Question{..}) id | 104 | preview (ctx . answer) >>= maybe (throwError Question{..}) id |
| 85 | 105 | ||
| 86 | d, z :: Integral a => Int -> FormulaM input a | 106 | d, z :: Integral a => Int -> FormulaM input a |
| 87 | d n = liftBase . fmap fromIntegral $ D.d n | 107 | d n = liftBase . fmap fromIntegral $ D.d n |
diff --git a/src/Sequence/Types.hs b/src/Sequence/Types.hs index 541505c..480dfee 100644 --- a/src/Sequence/Types.hs +++ b/src/Sequence/Types.hs | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | {-# LANGUAGE FlexibleInstances, FlexibleContexts, MultiParamTypeClasses, TypeSynonymInstances, ViewPatterns, OverloadedStrings, TemplateHaskell, GeneralizedNewtypeDeriving #-} | 1 | {-# LANGUAGE FlexibleInstances, FlexibleContexts, MultiParamTypeClasses, TypeSynonymInstances, ViewPatterns, OverloadedStrings, TemplateHaskell, GeneralizedNewtypeDeriving, TypeOperators #-} |
| 2 | 2 | ||
| 3 | module Sequence.Types | 3 | module Sequence.Types |
| 4 | ( GameState, gEntities, gEntityNames, gFocus, gNextId' | 4 | ( GameState, gEntities, gEntityNames, gFocus, gNextId' |
| @@ -33,6 +33,7 @@ import Data.Tuple | |||
| 33 | import Data.Ord | 33 | import Data.Ord |
| 34 | 34 | ||
| 35 | import Sequence.Contact.Types | 35 | import Sequence.Contact.Types |
| 36 | import Sequence.Formula ((:<:)(..)) | ||
| 36 | 37 | ||
| 37 | import Text.Read (readMaybe) | 38 | import Text.Read (readMaybe) |
| 38 | 39 | ||
| @@ -77,6 +78,9 @@ instance Default Entity where | |||
| 77 | , _eStats = def | 78 | , _eStats = def |
| 78 | } | 79 | } |
| 79 | 80 | ||
| 81 | instance Stats :<: Entity where | ||
| 82 | ctx' = eStats | ||
| 83 | |||
| 80 | newtype EntityName = EntityName { _entityName :: CI String } | 84 | newtype EntityName = EntityName { _entityName :: CI String } |
| 81 | deriving (Show, Eq, Ord) | 85 | deriving (Show, Eq, Ord) |
| 82 | 86 | ||
