summaryrefslogtreecommitdiff
path: root/src/Sequence/Formula.hs
blob: 7a6e689ea580499e28640acba9c58dfcbb5545a1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
{-# LANGUAGE RecordWildCards, RankNTypes, TypeSynonymInstances, FlexibleInstances #-}

module Sequence.Formula
  ( FormulaM
  , evalFormula
  , val
  ) where

import Control.Lens

import Control.Monad.Trans.Either
import Control.Monad.Reader
import Numeric.Probability.Game.Event

import Sequence.Utils

import Text.Read (readMaybe)

type FormulaM input a = ReaderT input (EitherT (Question input) EventM) a

data Question input = Question
                      { answer :: Lens' input (Maybe Int)
                      , prompt :: String
                      }

instance Integral a => Num (FormulaM input a) where
  (+) x y = (+) <$> x <*> y
  (-) x y = (-) <$> x <*> y
  negate = fmap negate
  abs = fmap abs
  signum = fmap signum
  (*) x y = sum <$> (flip replicateM y =<< fromIntegral <$> x)
  fromInteger = return . fromInteger

askQuestion :: MonadIO m => input -> (Question input) -> m input
askQuestion input q@(Question{..}) = flip (set answer) input <$> askQ prompt (join . fmap readMaybe)

evalFormula :: MonadIO m => input -> FormulaM input a -> m a
evalFormula input formula = do
  result <- liftIO . enact . runEitherT . (runReaderT ?? input) $ formula
  case result of
    Left q -> askQuestion input q >>= flip evalFormula formula
    Right result -> return result

val :: Integral a => Lens' input (Maybe Int) -> String -> FormulaM input Int
val answer prompt = view answer >>= maybe (lift . left $ Question{..}) return