{-# LANGUAGE
DeriveFunctor
, FlexibleInstances
, FunctionalDependencies
, GeneralizedNewtypeDeriving
, NamedFieldPuns
, OverloadedStrings
, RankNTypes
, ScopedTypeVariables
, StandaloneDeriving
, TypeFamilies
, LiberalTypeSynonyms
, TypeSynonymInstances
, UndecidableInstances
, DataKinds
, KindSignatures
#-}
module Ditto.Core (
FormState
, Form(..)
, Environment(..)
, NoEnvironment(..)
, WithEnvironment(..)
, noEnvironment
, (@$)
, catchFormError
, catchFormErrorM
, eitherForm
, getFormId
, getFormInput
, getFormInput'
, getFormRange
, getNamedFormId
, incrementFormId
, isInRange
, mapFormMonad
, mapResult
, mapView
, mkOk
, retainChildErrors
, retainErrors
, runForm
, runForm_
, successDecode
, unitRange
, view
, viewForm
, pureRes
, liftForm
) where
import Control.Applicative
import Control.Monad.Reader
import Control.Monad.State.Lazy
import Data.Bifunctor
import Data.Text (Text)
import Ditto.Types
import Ditto.Backend
import Torsor
type FormState m = StateT FormRange m
data Form m input err view a = Form
{ formDecodeInput :: input -> m (Either err a)
, formInitialValue :: m a
, formFormlet :: FormState m (View err view, Result err (Proved a))
} deriving (Functor)
instance (Monad m, Monoid view) => Applicative (Form m input err view) where
pure x = Form (successDecode x) (pure x) $ do
i <- getFormId
pure ( mempty
, Ok $ Proved
{ pos = FormRange i i
, unProved = x
}
)
(Form df ivF frmF) <*> (Form da ivA frmA) =
Form
( \inp -> do
f <- df inp
x <- da inp
pure (f <*> x) )
(ivF <*> ivA)
( do
((view1, fok), (view2, aok)) <-
bracketState $ do
res1 <- frmF
incrementFormRange
res2 <- frmA
pure (res1, res2)
case (fok, aok) of
(Error errs1, Error errs2) -> pure (view1 <> view2, Error $ errs1 ++ errs2)
(Error errs1, _) -> pure (view1 <> view2, Error errs1)
(_, Error errs2) -> pure (view1 <> view2, Error errs2)
(Ok (Proved (FormRange x _) f), Ok (Proved (FormRange _ y) a)) ->
pure
( view1 <> view2
, Ok $ Proved
{ pos = FormRange x y
, unProved = f a
}
)
)
f1 *> f2 = Form (formDecodeInput f2) (formInitialValue f2) $ do
(v2, r) <- formFormlet f2
(v1, _) <- formFormlet f1
pure (v1 <> v2, r)
f1 <* f2 = Form (formDecodeInput f1) (formInitialValue f1) $ do
(v1, r) <- formFormlet f1
(v2, _) <- formFormlet f2
pure (v1 <> v2, r)
instance (Environment m input, Monoid view, FormError input err) => Monad (Form m input err view) where
form >>= f =
let mres = fmap snd $ runForm "" form
in Form
(\input -> do
res <- mres
case res of
Error {} -> do
iv <- formInitialValue form
formDecodeInput (f iv) input
Ok (Proved _ x) -> formDecodeInput (f x) input
)
(do
res <- mres
case res of
Error {} -> do
iv <- formInitialValue form
formInitialValue $ f iv
Ok (Proved _ x) -> formInitialValue (f x)
)
(do
(View viewF0, res0) <- formFormlet form
case res0 of
Error errs0 -> do
iv <- lift $ formInitialValue form
(View viewF, res) <- formFormlet $ f iv
let errs = case res of
Error es -> es
Ok {} -> []
pure (View $ const $ viewF0 errs0 <> viewF errs, Error (errs0 <> errs))
Ok (Proved _ x) -> fmap (first (\(View v) -> View $ \e -> viewF0 [] <> v e)) $ formFormlet (f x)
)
return = pure
(>>) = (*>)
instance (Monad m, Monoid view, Semigroup a) => Semigroup (Form m input err view a) where
(<>) = liftA2 (<>)
instance (Monad m, Monoid view, Monoid a) => Monoid (Form m input err view a) where
mempty = pure mempty
instance Functor m => Bifunctor (Form m input err) where
first = mapView
second = fmap
errorInitialValue :: String
errorInitialValue = "ditto: Ditto.Core.errorInitialValue was evaluated"
instance (Monad m, Monoid view, FormError input err, Environment m input) => Alternative (Form m input err view) where
empty = Form
failDecodeMDF
(error errorInitialValue)
(pure (mempty, Error []))
formA <|> formB = do
efA <- formEither formA
case efA of
Right{} -> formA
Left{} -> formB
class Monad m => Environment m input | m -> input where
environment :: FormId -> m (Value input)
newtype NoEnvironment input m a = NoEnvironment { getNoEnvironment :: m a }
deriving (Monad, Functor, Applicative)
instance Monad m => Environment (NoEnvironment input m) input where
environment = noEnvironment
noEnvironment :: Applicative m => FormId -> m (Value input)
noEnvironment = const (pure Default)
newtype WithEnvironment input m a = WithEnvironment { getWithEnvironment :: ReaderT (FormId -> m (Value input)) m a }
deriving (Monad, Functor, Applicative)
deriving instance Monad m => MonadReader (FormId -> m (Value input)) (WithEnvironment input m)
instance MonadTrans (WithEnvironment input) where
lift = WithEnvironment . lift
instance Monad m => Environment (WithEnvironment input m) input where
environment fid = do
f <- ask
lift $ f fid
failDecodeMDF :: forall m input err a. (Applicative m, FormError input err) => input -> m (Either err a)
failDecodeMDF = const $ pure $ Left err
where
mdf :: CommonFormError input
mdf = MissingDefaultValue
err :: err
err = commonFormError mdf
successDecode :: Applicative m => a -> (input -> m (Either err a))
successDecode = const . pure . Right
mapView :: (Functor m)
=> (view -> view')
-> Form m input err view a
-> Form m input err view' a
mapView f Form{formDecodeInput, formInitialValue, formFormlet} =
Form formDecodeInput formInitialValue (fmap (first (fmap f)) formFormlet)
incrementFormId :: FormId -> FormId
incrementFormId fid = add 1 fid
isInRange
:: FormId
-> FormRange
-> Bool
isInRange a (FormRange b c) =
formIdentifier a >= formIdentifier b
&& formIdentifier a < formIdentifier c
isSubRange
:: FormRange
-> FormRange
-> Bool
isSubRange (FormRange a b) (FormRange c d) =
formIdentifier a >= formIdentifier c
&& formIdentifier b <= formIdentifier d
getFormId :: Monad m => FormState m FormId
getFormId = do
FormRange x _ <- get
pure x
getFormRange :: Monad m => FormState m FormRange
getFormRange = get
getNamedFormId :: Monad m => Text -> FormState m FormId
getNamedFormId name = do
FormRange x _ <- get
pure $ FormIdName name $ formIdentifier x
unitRange :: FormId -> FormRange
unitRange i = FormRange i $ add 1 i
bracketState :: Monad m => FormState m a -> FormState m a
bracketState k = do
FormRange startF1 _ <- get
res <- k
FormRange _ endF2 <- get
put $ FormRange startF1 endF2
pure res
incrementFormRange :: Monad m => FormState m ()
incrementFormRange = do
FormRange _ endF1 <- get
put $ unitRange endF1
runForm :: Monad m
=> Text
-> Form m input err view a
-> m (View err view, Result err (Proved a))
runForm prefix Form{formFormlet} =
evalStateT formFormlet (unitRange (FormId prefix (pure 0)))
runForm_ :: (Monad m)
=> Text
-> Form m input err view a
-> m (view , Maybe a)
runForm_ prefix form = do
(view', result) <- runForm prefix form
pure $ case result of
Error e -> (unView view' e , Nothing)
Ok x -> (unView view' [], Just (unProved x))
eitherForm :: (Monad m)
=> Text
-> Form m input err view a
-> m (Either view a)
eitherForm id' form = do
(view', result) <- runForm id' form
return $ case result of
Error e -> Left $ unView view' e
Ok x -> Right (unProved x)
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 (View err view, Result err (Proved a))
mkOk i view' val = pure
( View $ const $ view'
, Ok ( Proved
{ pos = unitRange i
, unProved = val
} )
)
formEither :: Monad m
=> Form m input err view a
-> Form m input err view (Either [err] a)
formEither Form{formDecodeInput, formInitialValue, formFormlet} = Form
(\input -> do
res <- formDecodeInput input
case res of
Left err -> pure $ Right $ Left [err]
Right x -> pure $ Right $ Right x
)
(fmap Right formInitialValue)
( do
range <- get
(view', res') <- formFormlet
let res = case res' of
Error err -> Left (map snd err)
Ok (Proved _ x) -> Right x
pure
( view'
, Ok $ Proved
{ pos = range
, unProved = res
}
)
)
getFormInput :: Environment m input => FormState m (Value input)
getFormInput = getFormId >>= getFormInput'
getFormInput' :: Environment m input => FormId -> FormState m (Value input)
getFormInput' fid = lift $ environment fid
retainErrors :: FormRange -> [(FormRange, e)] -> [e]
retainErrors range = map snd . filter ((== range) . fst)
retainChildErrors :: FormRange -> [(FormRange, e)] -> [e]
retainChildErrors range = map snd . filter ((`isSubRange` range) . fst)
view :: Monad m => view -> Form m input err view ()
view html = Form (successDecode ()) (pure ()) $ do
i <- getFormId
pure ( View (const html)
, Ok $ Proved
{ pos = FormRange i i
, unProved = ()
}
)
mapFormMonad :: (Monad f)
=> (forall x. m x -> f x)
-> Form m input err view a
-> Form f input err view a
mapFormMonad f Form{formDecodeInput, formInitialValue, formFormlet} = Form
{ formDecodeInput = f . formDecodeInput
, formInitialValue = f formInitialValue
, formFormlet = do
(view', res) <- fstate formFormlet
pure $ (view', res)
}
where
fstate st = StateT $ f . runStateT st
catchFormError :: (Monad m)
=> ([err] -> a)
-> Form m input err view a
-> Form m input err view a
catchFormError ferr Form{formDecodeInput, formInitialValue, formFormlet} = Form formDecodeInput formInitialValue $ do
i <- getFormId
(View viewf, res) <- formFormlet
case res of
Ok _ -> formFormlet
Error err -> mkOk i (viewf []) (ferr $ fmap snd err)
catchFormErrorM :: (Monad m)
=> Form m input err view a
-> ([err] -> Form m input err view a)
-> Form m input err view a
catchFormErrorM form@(Form{formDecodeInput, formInitialValue}) e = Form formDecodeInput formInitialValue $ do
(_, res0) <- formFormlet form
case res0 of
Ok _ -> formFormlet form
Error err -> formFormlet $ e $ map snd err
mapResult :: (Monad m)
=> (Result err (Proved a) -> Result err (Proved a))
-> (View err view -> View err view)
-> Form m input err view a
-> Form m input err view a
mapResult fres fview Form{formDecodeInput, formInitialValue, formFormlet} = Form formDecodeInput formInitialValue $ do
(view', res) <- formFormlet
pure (fview view', fres res)
viewForm :: (Monad m)
=> Text
-> Form m input err view a
-> m view
viewForm prefix form = do
(v, _) <- getNoEnvironment $ runForm prefix $ mapFormMonad NoEnvironment form
pure (unView v [])
pureRes :: (Monad m, Monoid view, FormError input err)
=> a
-> Either err a
-> Form m input err view a
pureRes def x' = case x' of
Right x -> Form (successDecode x) (pure x) $ do
i <- getFormId
pure ( mempty
, Ok $ Proved
{ pos = FormRange i i
, unProved = x
}
)
Left e -> Form (successDecode def) (pure def) $ do
i <- getFormId
pure ( mempty
, Error [(FormRange i i, e)]
)
liftForm :: (Monad m, Monoid view) => m a -> Form m input err view a
liftForm x = Form (const (fmap Right x)) x $ do
res <- lift x
i <- getFormId
pure (mempty, Ok $ Proved (FormRange i i) res)