{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Ditto.Core where
import Control.Applicative (Applicative(..), Alternative(..))
import Control.Monad.Reader (MonadReader (ask), ReaderT, runReaderT)
import Control.Monad.State (MonadState (get, put), StateT, evalStateT)
import Control.Monad.Trans (lift)
import Data.Bifunctor (Bifunctor (..))
import Data.List.NonEmpty (NonEmpty(..))
import Data.Monoid (Monoid (mappend, mempty))
import Data.Text.Lazy (Text, unpack)
import Ditto.Result (FormId (..), FormRange (..), Result (..), unitRange, zeroId)
data Proved a = Proved
{ pos :: FormRange
, unProved :: a
}
deriving (Show, Functor)
unitProved :: FormId -> Proved ()
unitProved formId =
Proved
{ pos = unitRange formId
, unProved = ()
}
type FormState m input = ReaderT (Environment m input) (StateT FormRange m)
data Value a
= Default
| Missing
| Found a
getFormInput :: Monad m => FormState m input (Value input)
getFormInput = getFormId >>= getFormInput'
getFormInput' :: Monad m => FormId -> FormState m input (Value input)
getFormInput' id' = do
env <- ask
case env of
NoEnvironment -> pure Default
Environment f -> lift $ lift $ f id'
getFormRange :: Monad m => FormState m i FormRange
getFormRange = get
data Environment m input
= Environment (FormId -> m (Value input))
| NoEnvironment
instance (Semigroup input, Monad m) => Semigroup (Environment m input) where
NoEnvironment <> x = x
x <> NoEnvironment = x
(Environment env1) <> (Environment env2) =
Environment $ \id' -> do
r1 <- (env1 id')
r2 <- (env2 id')
case (r1, r2) of
(Missing, Missing) -> pure Missing
(Default, Missing) -> pure Default
(Missing, Default) -> pure Default
(Default, Default) -> pure Default
(Found x, Found y) -> pure $ Found (x <> y)
(Found x, _) -> pure $ Found x
(_, Found y) -> pure $ Found y
instance (Semigroup input, Monad m) => Monoid (Environment m input) where
mempty = NoEnvironment
mappend = (<>)
getFormId :: Monad m => FormState m i FormId
getFormId = do
FormRange x _ <- get
pure x
getNamedFormId :: Monad m => String -> FormState m i FormId
getNamedFormId name = do
FormRange x _ <- get
pure $ case x of
FormIdCustom _ i -> FormIdCustom name i
FormId _ (i :| _) -> FormIdCustom name i
incFormId :: Monad m => FormState m i ()
incFormId = do
FormRange _ endF1 <- get
put $ unitRange endF1
newtype View err v
= View { unView :: [(FormRange, err)] -> v }
deriving (Semigroup, Monoid, Functor)
newtype Form m input err view a = Form {unForm :: FormState m input (View err view, m (Result err (Proved a)))}
deriving (Functor)
bracketState :: Monad m => FormState m input a -> FormState m input a
bracketState k = do
FormRange startF1 _ <- get
res <- k
FormRange _ endF2 <- get
put $ FormRange startF1 endF2
pure res
instance (Functor m, Monoid view, Monad m) => Applicative (Form m input err view) where
pure a =
Form $ do
i <- getFormId
pure
( View $ const $ mempty
, pure $ Ok $ Proved
{ pos = FormRange i i
, unProved = a
}
)
(Form frmF) <*> (Form frmA) =
Form $ do
((view1, mfok), (view2, maok)) <-
bracketState $ do
res1 <- frmF
incFormId
res2 <- frmA
pure (res1, res2)
fok <- lift $ lift $ mfok
aok <- lift $ lift $ maok
case (fok, aok) of
(Error errs1, Error errs2) -> pure (view1 <> view2, pure $ Error $ errs1 ++ errs2)
(Error errs1, _) -> pure (view1 <> view2, pure $ Error $ errs1)
(_, Error errs2) -> pure (view1 <> view2, pure $ Error $ errs2)
(Ok (Proved (FormRange x _) f), Ok (Proved (FormRange _ y) a)) ->
pure
( view1 <> view2
, pure $ Ok $ Proved
{ pos = FormRange x y
, unProved = f a
}
)
instance (Monad m, Monoid view) => Alternative (Form m input err view) where
empty = Form $ pure (mempty, pure $ Error mempty)
formA <|> formB = Form $ do
(_, mres0) <- unForm formA
res0 <- lift $ lift mres0
case res0 of
Ok _ -> unForm formA
Error _ -> unForm formB
instance Functor m => Bifunctor (Form m input err) where
first = mapView
second = fmap
instance (Monad m, Monoid view, Semigroup a) => Semigroup (Form m input err view a) where
(<>) = liftA2 (<>)
instance (Monoid view, Monad m, Semigroup a) => Monoid (Form m input err view a) where
mempty = Form $ pure (mempty, pure $ Error mempty)
newtype MForm m input err view a = MForm { runMForm :: Form m input err view a }
deriving (Functor, Bifunctor, Alternative, Applicative)
instance (Monad m, Monoid view) => Monad (MForm m input err view) where
(MForm formA) >>= formFunction = MForm $ Form $ do
(view0, mfok) <- unForm formA
fok :: Result err (Proved a) <- lift $ lift mfok
case fok of
Ok x -> do
(view1, mfok1) <- unForm $ runMForm $ formFunction $ unProved x
pure
( view0 <> view1
, mfok1
)
Error errs -> pure (view0, pure $ Error errs)
runAsMForm
:: (Monad m)
=> Environment m input
-> Text
-> Form m input err view a
-> m (View err view, m (Result err (Proved a)))
runAsMForm env prefix' = runForm env prefix' . runMForm . MForm
runForm
:: (Monad m)
=> Environment m input
-> Text
-> Form m input err view a
-> m (View err view, m (Result err (Proved a)))
runForm env prefix' form =
evalStateT (runReaderT (unForm form) env) (unitRange (zeroId $ unpack prefix'))
runForm'
:: (Monad m)
=> Environment m input
-> Text
-> Form m input err view a
-> m (view, Maybe a)
runForm' env prefix form = do
(view', mresult) <- runForm env prefix form
result <- mresult
pure $ case result of
Error e -> (unView view' e, Nothing)
Ok x -> (unView view' [], Just (unProved x))
viewForm
:: (Monad m)
=> Text
-> Form m input err view a
-> m view
viewForm prefix form = do
(v, _) <- runForm NoEnvironment prefix form
pure (unView v [])
eitherForm
:: (Monad m)
=> Environment m input
-> Text
-> Form m input err view a
-> m (Either view a)
eitherForm env id' form = do
(view', mresult) <- runForm env id' form
result <- mresult
pure $ case result of
Error e -> Left $ unView view' e
Ok x -> Right (unProved x)
view
:: (Monad m)
=> view
-> Form m input err view ()
view view' = Form $ do
i <- getFormId
pure
( View (const view')
, pure
( Ok
( Proved
{ pos = FormRange i i
, unProved = ()
}
)
)
)
(++>)
:: (Monad m, Semigroup view)
=> Form m input err view z
-> Form m input err view a
-> Form m input err view a
f1 ++> f2 = Form $ do
(v2, r) <- unForm f2
(v1, _) <- unForm f1
pure (v1 <> v2, r)
infixl 6 ++>
(<++)
:: (Monad m, Semigroup view)
=> Form m input err view a
-> Form m input err view z
-> Form m input err view a
f1 <++ f2 = Form $ do
(v1, r) <- unForm f1
(v2, _) <- unForm f2
pure (v1 <> v2, r)
infixr 5 <++
mapView
:: (Functor m)
=> (view -> view')
-> Form m input err view a
-> Form m input err view' a
mapView f = Form . fmap (first $ fmap f) . unForm
infixr 0 @$
(@$) :: Monad m => (view -> view) -> Form m input err view a -> Form m input err view a
(@$) = mapView
mkOk
:: (Monad m)
=> FormId
-> view
-> a
-> FormState m input (View err view, m (Result err (Proved a)))
mkOk i view' val =
pure
( View $ const $ view'
, pure $
Ok
( Proved
{ pos = unitRange i
, unProved = val
}
)
)