packages feed

ditto 0.1.3.0 → 0.2

raw patch · 8 files changed

+262/−206 lines, 8 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Ditto.Core: instance (GHC.Base.Functor m, GHC.Base.Monoid view, GHC.Base.Monad m, x Data.Type.Equality.~ ()) => GHC.Base.Applicative (Ditto.Core.Form m input error view)
- Ditto.Core: instance GHC.Base.Functor (Ditto.Core.View e)
- Ditto.Core: instance GHC.Base.Functor m => GHC.Base.Functor (Ditto.Core.Form m input error view)
- Ditto.Core: instance GHC.Base.Monoid v => GHC.Base.Monoid (Ditto.Core.View error v)
- Ditto.Core: instance GHC.Base.Semigroup v => GHC.Base.Semigroup (Ditto.Core.View error v)
- Ditto.Result: [formIdList] :: FormId -> NonEmpty Int
- Ditto.Result: [formPrefix] :: FormId -> String
+ Ditto.Backend: commonFormErrorText :: (input -> Text) -> CommonFormError input -> Text
+ Ditto.Backend: instance Ditto.Backend.FormError Data.Text.Internal.Text
+ Ditto.Core: (@$) :: Monad m => (view -> view) -> Form m input err view a -> Form m input err view a
+ Ditto.Core: MForm :: Form m input err view a -> MForm m input err view a
+ Ditto.Core: [runMForm] :: MForm m input err view a -> Form m input err view a
+ Ditto.Core: infixr 0 @$
+ Ditto.Core: instance (GHC.Base.Functor m, GHC.Base.Monoid view, GHC.Base.Monad m) => GHC.Base.Applicative (Ditto.Core.Form m input err view)
+ Ditto.Core: instance (GHC.Base.Monad m, GHC.Base.Monoid view) => GHC.Base.Alternative (Ditto.Core.Form m input err view)
+ Ditto.Core: instance (GHC.Base.Monad m, GHC.Base.Monoid view) => GHC.Base.Alternative (Ditto.Core.MForm m input err view)
+ Ditto.Core: instance (GHC.Base.Monad m, GHC.Base.Monoid view) => GHC.Base.Monad (Ditto.Core.MForm m input err view)
+ Ditto.Core: instance (GHC.Base.Monad m, GHC.Base.Monoid view, GHC.Base.Semigroup a) => GHC.Base.Semigroup (Ditto.Core.Form m input err view a)
+ Ditto.Core: instance (GHC.Base.Monoid view, GHC.Base.Monad m) => GHC.Base.Applicative (Ditto.Core.MForm m input err view)
+ Ditto.Core: instance (GHC.Base.Monoid view, GHC.Base.Monad m, GHC.Base.Semigroup a) => GHC.Base.Monoid (Ditto.Core.Form m input err view a)
+ Ditto.Core: instance GHC.Base.Functor (Ditto.Core.View err)
+ Ditto.Core: instance GHC.Base.Functor m => Data.Bifunctor.Bifunctor (Ditto.Core.Form m input err)
+ Ditto.Core: instance GHC.Base.Functor m => Data.Bifunctor.Bifunctor (Ditto.Core.MForm m input err)
+ Ditto.Core: instance GHC.Base.Functor m => GHC.Base.Functor (Ditto.Core.Form m input err view)
+ Ditto.Core: instance GHC.Base.Functor m => GHC.Base.Functor (Ditto.Core.MForm m input err view)
+ Ditto.Core: instance GHC.Base.Monoid v => GHC.Base.Monoid (Ditto.Core.View err v)
+ Ditto.Core: instance GHC.Base.Semigroup v => GHC.Base.Semigroup (Ditto.Core.View err v)
+ Ditto.Core: newtype MForm m input err view a
+ Ditto.Core: runAsMForm :: Monad m => Environment m input -> Text -> Form m input err view a -> m (View err view, m (Result err (Proved a)))
+ Ditto.Generalized: inputMaybeReq :: (Monad m, FormError err) => (input -> Either err a) -> (FormId -> Maybe a -> view) -> Maybe a -> Form m input err view a
+ Ditto.Generalized.Named: inputMaybeReq :: (Monad m, FormError err) => String -> (input -> Either err a) -> (FormId -> Maybe a -> view) -> Maybe a -> Form m input err view a
- Ditto.Core: (++>) :: (Monad m, Semigroup view) => Form m input error view () -> Form m input error view a -> Form m input error view a
+ Ditto.Core: (++>) :: (Monad m, Semigroup view) => Form m input err view z -> Form m input err view a -> Form m input err view a
- Ditto.Core: (<++) :: (Monad m, Semigroup view) => Form m input error view a -> Form m input error view () -> Form m input error view a
+ Ditto.Core: (<++) :: (Monad m, Semigroup view) => Form m input err view a -> Form m input err view z -> Form m input err view a
- Ditto.Core: Form :: FormState m input (View error view, m (Result error (Proved a))) -> Form m input error view a
+ Ditto.Core: Form :: FormState m input (View err view, m (Result err (Proved a))) -> Form m input err view a
- Ditto.Core: View :: ([(FormRange, error)] -> v) -> View error v
+ Ditto.Core: View :: ([(FormRange, err)] -> v) -> View err v
- Ditto.Core: [unForm] :: Form m input error view a -> FormState m input (View error view, m (Result error (Proved a)))
+ Ditto.Core: [unForm] :: Form m input err view a -> FormState m input (View err view, m (Result err (Proved a)))
- Ditto.Core: [unView] :: View error v -> [(FormRange, error)] -> v
+ Ditto.Core: [unView] :: View err v -> [(FormRange, err)] -> v
- Ditto.Core: eitherForm :: Monad m => Environment m input -> Text -> Form m input error view a -> m (Either view a)
+ Ditto.Core: eitherForm :: Monad m => Environment m input -> Text -> Form m input err view a -> m (Either view a)
- Ditto.Core: mapView :: (Monad m, Functor m) => (view -> view') -> Form m input error view a -> Form m input error view' a
+ Ditto.Core: mapView :: Functor m => (view -> view') -> Form m input err view a -> Form m input err view' a
- Ditto.Core: mkOk :: Monad m => FormId -> view -> a -> FormState m input (View error view, m (Result error (Proved a)))
+ Ditto.Core: mkOk :: Monad m => FormId -> view -> a -> FormState m input (View err view, m (Result err (Proved a)))
- Ditto.Core: newtype Form m input error view a
+ Ditto.Core: newtype Form m input err view a
- Ditto.Core: newtype View error v
+ Ditto.Core: newtype View err v
- Ditto.Core: runForm :: Monad m => Environment m input -> Text -> Form m input error view a -> m (View error view, m (Result error (Proved a)))
+ Ditto.Core: runForm :: Monad m => Environment m input -> Text -> Form m input err view a -> m (View err view, m (Result err (Proved a)))
- Ditto.Core: runForm' :: Monad m => Environment m input -> Text -> Form m input error view a -> m (view, Maybe a)
+ Ditto.Core: runForm' :: Monad m => Environment m input -> Text -> Form m input err view a -> m (view, Maybe a)
- Ditto.Core: view :: Monad m => view -> Form m input error view ()
+ Ditto.Core: view :: Monad m => view -> Form m input err view ()
- Ditto.Core: viewForm :: Monad m => Text -> Form m input error view a -> m view
+ Ditto.Core: viewForm :: Monad m => Text -> Form m input err view a -> m view
- Ditto.Generalized: inputChoice :: forall a m err input lbl view. (Functor m, FormError err, ErrorInputType err ~ input, FormInput input, Monad m) => (a -> Bool) -> [(a, lbl)] -> (FormId -> [(FormId, Int, lbl, Bool)] -> view) -> Form m input err view a
+ Ditto.Generalized: inputChoice :: forall a m err input lbl view. (FormError err, ErrorInputType err ~ input, FormInput input, Monad m) => (a -> Bool) -> [(a, lbl)] -> (FormId -> [(FormId, Int, lbl, Bool)] -> view) -> Form m input err view a
- Ditto.Generalized: inputChoiceForms :: forall a m err input lbl view. (Functor m, Monad m, FormError err, ErrorInputType err ~ input, FormInput input) => a -> [(Form m input err view a, lbl)] -> (FormId -> [(FormId, Int, FormId, view, lbl, Bool)] -> view) -> Form m input err view a
+ Ditto.Generalized: inputChoiceForms :: forall a m err input lbl view. (Monad m, FormError err, ErrorInputType err ~ input, FormInput input) => a -> [(Form m input err view a, lbl)] -> (FormId -> [(FormId, Int, FormId, view, lbl, Bool)] -> view) -> Form m input err view a
- Ditto.Generalized: inputMaybe :: (Monad m, FormError err) => (input -> Either err a) -> (FormId -> a -> view) -> a -> Form m input err view (Maybe a)
+ Ditto.Generalized: inputMaybe :: (Monad m, FormError err) => (input -> Either err a) -> (FormId -> Maybe a -> view) -> Maybe a -> Form m input err view (Maybe a)
- Ditto.Generalized: inputMulti :: forall m input err view a lbl. (Functor m, FormError err, ErrorInputType err ~ input, FormInput input, Monad m) => [(a, lbl)] -> (FormId -> [(FormId, Int, lbl, Bool)] -> view) -> (a -> Bool) -> Form m input err view [a]
+ Ditto.Generalized: inputMulti :: forall m input err view a lbl. (FormError err, ErrorInputType err ~ input, FormInput input, Monad m) => [(a, lbl)] -> (FormId -> [(FormId, Int, lbl, Bool)] -> view) -> (a -> Bool) -> Form m input err view [a]
- Ditto.Generalized: inputNoData :: Monad m => (FormId -> a -> view) -> a -> Form m input err view ()
+ Ditto.Generalized: inputNoData :: Monad m => (FormId -> view) -> Form m input err view ()
- Ditto.Generalized.Named: inputChoice :: forall a m err input lbl view. (Functor m, FormError err, ErrorInputType err ~ input, FormInput input, Monad m) => String -> (a -> Bool) -> [(a, lbl)] -> (FormId -> [(FormId, Int, lbl, Bool)] -> view) -> Form m input err view a
+ Ditto.Generalized.Named: inputChoice :: forall a m err input lbl view. (FormError err, ErrorInputType err ~ input, FormInput input, Monad m) => String -> (a -> Bool) -> [(a, lbl)] -> (FormId -> [(FormId, Int, lbl, Bool)] -> view) -> Form m input err view a
- Ditto.Generalized.Named: inputChoiceForms :: forall a m err input lbl view. (Functor m, Monad m, FormError err, ErrorInputType err ~ input, FormInput input) => String -> a -> [(Form m input err view a, lbl)] -> (FormId -> [(FormId, Int, FormId, view, lbl, Bool)] -> view) -> Form m input err view a
+ Ditto.Generalized.Named: inputChoiceForms :: forall a m err input lbl view. (Monad m, FormError err, ErrorInputType err ~ input, FormInput input) => String -> a -> [(Form m input err view a, lbl)] -> (FormId -> [(FormId, Int, FormId, view, lbl, Bool)] -> view) -> Form m input err view a
- Ditto.Generalized.Named: inputMaybe :: (Monad m, FormError err) => String -> (input -> Either err a) -> (FormId -> a -> view) -> a -> Form m input err view (Maybe a)
+ Ditto.Generalized.Named: inputMaybe :: (Monad m, FormError err) => String -> (input -> Either err a) -> (FormId -> Maybe a -> view) -> Maybe a -> Form m input err view (Maybe a)
- Ditto.Generalized.Named: inputMulti :: forall m input err view a lbl. (Functor m, FormError err, ErrorInputType err ~ input, FormInput input, Monad m) => String -> [(a, lbl)] -> (FormId -> [(FormId, Int, lbl, Bool)] -> view) -> (a -> Bool) -> Form m input err view [a]
+ Ditto.Generalized.Named: inputMulti :: forall m input err view a lbl. (FormError err, ErrorInputType err ~ input, FormInput input, Monad m) => String -> [(a, lbl)] -> (FormId -> [(FormId, Int, lbl, Bool)] -> view) -> (a -> Bool) -> Form m input err view [a]
- Ditto.Generalized.Named: inputNoData :: Monad m => String -> (FormId -> a -> view) -> a -> Form m input err view ()
+ Ditto.Generalized.Named: inputNoData :: Monad m => String -> (FormId -> view) -> Form m input err view ()

Files

ditto.cabal view
@@ -1,5 +1,5 @@ Name:          ditto-Version:       0.1.3.0+Version:       0.2 Synopsis:      ditto is a type-safe HTML form generation and validation library Description:   ditto follows in the footsteps of formlets and                digestive-functors <= 0.2. It provides a
src/Ditto/Backend.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE OverloadedStrings #-}  {- | This module contains two classes. 'FormInput' is a class which is parameterized over the @input@ type used to represent form data in different web frameworks. There should be one instance for each framework, such as Happstack, Snap, WAI, etc.@@ -32,19 +33,35 @@   :: (input -> String) -- ^ show 'input' in a format suitable for error messages   -> CommonFormError input -- ^ a 'CommonFormError'   -> String-commonFormErrorStr showInput cfe =-  case cfe of-    (InputMissing formId) -> "Input field missing for " ++ show formId-    (NoStringFound input) -> "Could not extract a string value from: " ++ showInput input-    (NoFileFound input) -> "Could not find a file associated with: " ++ showInput input-    (MultiFilesFound input) -> "Found multiple files associated with: " ++ showInput input-    (MultiStringsFound input) -> "Found multiple strings associated with: " ++ showInput input-    MissingDefaultValue -> "Missing default value."+commonFormErrorStr showInput cfe = case cfe of+  InputMissing formId -> "Input field missing for " ++ show formId+  NoStringFound input -> "Could not extract a string value from: " ++ showInput input+  NoFileFound input -> "Could not find a file associated with: " ++ showInput input+  MultiFilesFound input -> "Found multiple files associated with: " ++ showInput input+  MultiStringsFound input -> "Found multiple strings associated with: " ++ showInput input+  MissingDefaultValue -> "Missing default value." +-- | some default error messages for 'CommonFormError'+commonFormErrorText+  :: (input -> Text) -- ^ show 'input' in a format suitable for error messages+  -> CommonFormError input -- ^ a 'CommonFormError'+  -> Text+commonFormErrorText showInput cfe = case cfe of+  InputMissing formId -> "Input field missing for " <> (T.pack $ show formId)+  NoStringFound input -> "Could not extract a string value from: " <> showInput input+  NoFileFound input -> "Could not find a file associated with: " <> showInput input+  MultiFilesFound input -> "Found multiple files associated with: " <> showInput input+  MultiStringsFound input -> "Found multiple strings associated with: " <> showInput input+  MissingDefaultValue -> "Missing default value."+ -- | A Class to lift a 'CommonFormError' into an application-specific error type class FormError e where   type ErrorInputType e   commonFormError :: (CommonFormError (ErrorInputType e)) -> e++instance FormError Text where+  type ErrorInputType Text = Text+  commonFormError = commonFormErrorText id  -- | Class which all backends should implement. --
src/Ditto/Core.hs view
@@ -1,12 +1,15 @@+{-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE ScopedTypeVariables #-}  {- | This module defines the 'Form' type, its instances, core manipulation functions, and a bunch of helper utilities. -} module Ditto.Core where -import Control.Applicative (Applicative ((<*>), pure))+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)@@ -15,22 +18,17 @@ import Data.Monoid (Monoid (mappend, mempty)) import Data.Text.Lazy (Text, unpack) import Ditto.Result (FormId (..), FormRange (..), Result (..), unitRange, zeroId)-import qualified Data.Semigroup as SG  ------------------------------------------------------------------------------ -- * Proved ------------------------------------------------------------------------------  -- | Proved records a value, the location that value came from, and something that was proved about the value.-data Proved a-  = Proved-      { pos :: FormRange-      , unProved :: a-      }-  deriving Show--instance Functor Proved where-  fmap f (Proved posi a) = Proved posi (f a)+data Proved a = Proved+  { pos :: FormRange+  , unProved :: a+  }+  deriving (Show, Functor)  -- | Utility Function: trivially prove nothing about () unitProved :: FormId -> Proved ()@@ -67,8 +65,7 @@   env <- ask   case env of     NoEnvironment -> pure Default-    Environment f ->-      lift $ lift $ f id'+    Environment f -> lift $ lift $ f id'  -- | Utility function: Get the current range --@@ -86,30 +83,26 @@   = Environment (FormId -> m (Value input))   | NoEnvironment -instance (SG.Semigroup input, Monad m) => SG.Semigroup (Environment m input) where-+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 SG.<> y)-          (Found x, _) -> pure $ Found x-          (_, Found y) -> pure $ Found y+    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  -- | Not quite sure when this is useful and so hard to say if the rules for combining things with Missing/Default are correct-instance (SG.Semigroup input, Monad m) => Monoid (Environment m input) where-+instance (Semigroup input, Monad m) => Monoid (Environment m input) where   mempty = NoEnvironment--  mappend = (SG.<>)+  mappend = (<>)  -- | Utility function: returns the current 'FormId'. This will only make sense -- if the form is not composed@@ -135,14 +128,9 @@ -- | A view represents a visual representation of a form. It is composed of a -- function which takes a list of all errors and then produces a new view ---newtype View error v-  = View-      { unView :: [(FormRange, error)] -> v-      }-  deriving (SG.Semigroup, Monoid)--instance Functor (View e) where-  fmap f (View g) = View $ f . g+newtype View err v+  = View { unView :: [(FormRange, err)] -> v }+  deriving (Semigroup, Monoid, Functor)  ------------------------------------------------------------------------------ -- * Form@@ -160,7 +148,7 @@ -- --   [@input@] A framework specific type for representing the raw key/value pairs from the form data -----   [@error@] A application specific type for error messages+--   [@err@] A application specific type for err messages -- --   [@view@] The type of data being generated for the view (HSP, Blaze Html, Heist, etc) --@@ -173,44 +161,8 @@ -- @digestive-functors <= 0.2@. If @proof@ is @()@, then 'Form' is an -- applicative functor and can be used almost exactly like -- @digestive-functors <= 0.2@.-newtype Form m input error view a = Form {unForm :: FormState m input (View error view, m (Result error (Proved a)))}---- instance (Monad m) => Functor (Form m input view error) where---   fmap f (Form frm) =---     Form $ do---       (view1, mval) <- frm---       val <- lift $ lift $ mval---       case val of---         (Ok (Proved posi a)) -> pure (view1, pure $ Ok (Proved posi (f a)))---         (Error errs) -> pure (view1, pure $ Error errs)---- instance (Semigroup view, Monad m) => Applicative (Form m input error view) where---   pure a =---     Form $ do---       i <- getFormId---       pure (mempty, pure $ Ok (Proved (unitRange i) 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---               }---             )+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@@ -220,11 +172,7 @@   put $ FormRange startF1 endF2   pure res -instance (Functor m) => Functor (Form m input error view) where-  fmap f form =-    Form $ fmap (second (fmap (fmap (fmap f)))) (unForm form)--instance (Functor m, Monoid view, Monad m, x ~ ()) => Applicative (Form m input error view) where+instance (Functor m, Monoid view, Monad m) => Applicative (Form m input err view) where   pure a =     Form $ do       i <- getFormId@@ -236,7 +184,6 @@           }         ) -  -- this coud be defined in terms of <<*>> if we just changed the proof of frmF to (() -> ())   (Form frmF) <*> (Form frmA) =     Form $ do       ((view1, mfok), (view2, maok)) <-@@ -260,47 +207,88 @@               }             ) +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)++-- | This provides a Monad instance which will stop rendering on err.+--   This instance isn't a part of @Form@ because of its undesirable behavior.+--   @-XApplicativeDo@ is generally preferred+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+ -- ** Ways to evaluate a Form  -- | Run a form--- runForm   :: (Monad m)   => Environment m input   -> Text-  -> Form m input error view a-  -> m (View error view, m (Result error (Proved a)))+  -> 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'))  -- | Run a form--- runForm'   :: (Monad m)   => Environment m input   -> Text-  -> Form m input error view a+  -> 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))+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))  -- | Just evaluate the form to a view. This usually maps to a GET request in the -- browser.--- viewForm   :: (Monad m)   => Text -- ^ form prefix-  -> Form m input error view a -- ^ form to view+  -> Form m input err view a -- ^ form to view   -> m view-viewForm prefix form =-  do-    (v, _) <- runForm NoEnvironment prefix form-    pure (unView v [])+viewForm prefix form = do+  (v, _) <- runForm NoEnvironment prefix form+  pure (unView v [])  -- | Evaluate a form --@@ -314,7 +302,7 @@   :: (Monad m)   => Environment m input -- ^ Input environment   -> Text -- ^ Identifier for the form-  -> Form m input error view a -- ^ Form to run+  -> Form m input err view a -- ^ Form to run   -> m (Either view a) -- ^ Result eitherForm env id' form = do   (view', mresult) <- runForm env id' form@@ -329,23 +317,22 @@ view   :: (Monad m)   => view -- ^ View to insert-  -> Form m input error view () -- ^ Resulting form-view view' =-  Form $ do-    i <- getFormId-    pure-      ( View (const view')-      , pure-        ( Ok-          ( Proved-            { pos = FormRange i i-            , unProved = ()-            }-          )+  -> Form m input err view () -- ^ Resulting form+view view' = Form $ do+  i <- getFormId+  pure+    ( View (const view')+    , pure+      ( Ok+        ( Proved+          { pos = FormRange i i+          , unProved = ()+          }         )       )+    ) --- | Append a unit form to the left. This is useful for adding labels or error+-- | Append a unit form to the left. This is useful for adding labels or err -- fields. -- -- The 'Forms' on the left and right hand side will share the same@@ -354,31 +341,28 @@ -- element. (++>)   :: (Monad m, Semigroup view)-  => Form m input error view ()-  -> Form m input error view a-  -> Form m input error view a-f1 ++> f2 =-  Form $ do-    -- Evaluate the form that matters first, so we have a correct range set-    (v2, r) <- unForm f2-    (v1, _) <- unForm f1-    pure (v1 <> v2, r)+  => Form m input err view z+  -> Form m input err view a+  -> Form m input err view a+f1 ++> f2 = Form $ do+  -- Evaluate the form that matters first, so we have a correct range set+  (v2, r) <- unForm f2+  (v1, _) <- unForm f1+  pure (v1 <> v2, r)  infixl 6 ++>  -- | Append a unit form to the right. See '++>'.--- (<++)   :: (Monad m, Semigroup view)-  => Form m input error view a-  -> Form m input error view ()-  -> Form m input error view a-f1 <++ f2 =-  Form $ do-    -- Evaluate the form that matters first, so we have a correct range set-    (v1, r) <- unForm f1-    (v2, _) <- unForm f2-    pure (v1 <> v2, r)+  => Form m input err view a+  -> Form m input err view z+  -> Form m input err view a+f1 <++ f2 = Form $ do+  -- Evaluate the form that matters first, so we have a correct range set+  (v1, r) <- unForm f1+  (v2, _) <- unForm f2+  pure (v1 <> v2, r)  infixr 5 <++ @@ -386,19 +370,24 @@ -- -- This is useful for wrapping a form inside of a \<fieldset\> or other markup element. mapView-  :: (Monad m, Functor m)+  :: (Functor m)   => (view -> view') -- ^ Manipulator-  -> Form m input error view a -- ^ Initial form-  -> Form m input error view' a -- ^ Resulting form+  -> Form m input err view a -- ^ Initial form+  -> Form m input err view' a -- ^ Resulting form mapView f = Form . fmap (first $ fmap f) . unForm +-- | infix mapView: succinct `foo @$ do ..`+infixr 0 @$+(@$) :: Monad m => (view -> view) -> Form m input err view a -> Form m input err view a+(@$) = mapView+ -- | Utility Function: turn a view and pure value into a successful 'FormState' mkOk   :: (Monad m)   => FormId   -> view   -> a-  -> FormState m input (View error view, m (Result error (Proved a)))+  -> FormState m input (View err view, m (Result err (Proved a))) mkOk i view' val =   pure     ( View $ const $ view'
src/Ditto/Generalized.hs view
@@ -18,16 +18,24 @@ inputMaybe   :: (Monad m, FormError err)   => (input -> Either err a)-  -> (FormId -> a -> view)-  -> a+  -> (FormId -> Maybe a -> view)+  -> Maybe a   -> Form m input err view (Maybe a) inputMaybe = G.inputMaybe getFormId +-- | used to construct elements with optional initial values, which are still required+inputMaybeReq+  :: (Monad m, FormError err)+  => (input -> Either err a)+  -> (FormId -> Maybe a -> view)+  -> Maybe a+  -> Form m input err view a+inputMaybeReq = G.inputMaybeReq getFormId+ -- | used for elements like @\<input type=\"reset\"\>@ which take a value, but are never present in the form data set. inputNoData   :: (Monad m)-  => (FormId -> a -> view)-  -> a+  => (FormId -> view)   -> Form m input err view () inputNoData = G.inputNoData getFormId @@ -40,7 +48,7 @@  -- | used for groups of checkboxes, @\<select multiple=\"multiple\"\>@ boxes inputMulti-  :: forall m input err view a lbl. (Functor m, FormError err, ErrorInputType err ~ input, FormInput input, Monad m)+  :: forall m input err view a lbl. (FormError err, ErrorInputType err ~ input, FormInput input, Monad m)   => [(a, lbl)] -- ^ value, label, initially checked   -> (FormId -> [(FormId, Int, lbl, Bool)] -> view) -- ^ function which generates the view   -> (a -> Bool) -- ^ isChecked/isSelected initially@@ -49,7 +57,7 @@  -- | radio buttons, single @\<select\>@ boxes inputChoice-  :: forall a m err input lbl view. (Functor m, FormError err, ErrorInputType err ~ input, FormInput input, Monad m)+  :: forall a m err input lbl view. (FormError err, ErrorInputType err ~ input, FormInput input, Monad m)   => (a -> Bool) -- ^ is default   -> [(a, lbl)] -- ^ value, label   -> (FormId -> [(FormId, Int, lbl, Bool)] -> view) -- ^ function which generates the view@@ -58,7 +66,7 @@  -- | radio buttons, single @\<select\>@ boxes inputChoiceForms-  :: forall a m err input lbl view. (Functor m, Monad m, FormError err, ErrorInputType err ~ input, FormInput input)+  :: forall a m err input lbl view. (Monad m, FormError err, ErrorInputType err ~ input, FormInput input)   => a   -> [(Form m input err view a, lbl)] -- ^ value, label   -> (FormId -> [(FormId, Int, FormId, view, lbl, Bool)] -> view) -- ^ function which generates the view@@ -97,3 +105,4 @@   -> Form m input err view a   -> Form m input err view a withErrors = G.withErrors+
src/Ditto/Generalized/Internal.hs view
@@ -19,7 +19,7 @@ -- | used for constructing elements like @\<input type=\"text\"\>@, which pure a single input value. input   :: (Monad m, FormError err)-  => FormState m input FormId +  => FormState m input FormId   -> (input -> Either err a)   -> (FormId -> a -> view)   -> a@@ -40,7 +40,7 @@                 }               )           )-      Found x -> case fromInput x of +      Found x -> case fromInput x of         Right a -> pure           ( View $ const $ toView i a           , pure $@@ -60,13 +60,58 @@         , pure $ Error [(unitRange i, commonFormError (InputMissing i))]         ) +inputMaybeReq+  :: (Monad m, FormError err)+  => FormState m input FormId+  -> (input -> Either err a)+  -> (FormId -> Maybe a -> view)+  -> Maybe a+  -> Form m input err view a+inputMaybeReq i' fromInput toView initialValue =+  Form $ do+    i <- i'+    v <- getFormInput' i+    case v of+      Default ->+        pure+          ( View $ const $ toView i initialValue+          , case initialValue of +              Just x -> pure $+                Ok ( Proved+                       { pos = unitRange i+                       , unProved = x+                       }+                   )+              Nothing -> pure $ Error [(unitRange i, commonFormError MissingDefaultValue)]+          )+      Found x -> case fromInput x of+        Right a -> pure+          ( View $ const $ toView i (Just a)+          , pure $+            Ok+              ( Proved+                { pos = unitRange i+                , unProved = a+                }+              )+          )+        Left err -> pure+          ( View $ const $ toView i initialValue+          , pure $ Error [(unitRange i, err)]+          )+      Missing -> pure+        ( View $ const $ toView i initialValue+        , pure $ Error [(unitRange i, commonFormError (InputMissing i))]+        )++ -- | used for elements like @\<input type=\"submit\"\>@ which are not always present in the form submission data. inputMaybe   :: (Monad m, FormError err)   => FormState m input FormId   -> (input -> Either err a)-  -> (FormId -> a -> view)-  -> a+  -> (FormId -> Maybe a -> view)+  -> Maybe a   -> Form m input err view (Maybe a) inputMaybe i' fromInput toView initialValue =   Form $ do@@ -79,13 +124,13 @@             Ok               ( Proved                 { pos = unitRange i-                , unProved = Just initialValue+                , unProved = initialValue                 }               )           )       Found x -> case fromInput x of         Right a -> pure-          ( View $ const $ toView i a+          ( View $ const $ toView i (Just a)           , pure $             Ok               ( Proved@@ -113,14 +158,13 @@ inputNoData   :: (Monad m)   => FormState m input FormId-  -> (FormId -> a -> view)-  -> a+  -> (FormId -> view)   -> Form m input err view ()-inputNoData i' toView a =+inputNoData i' toView =   Form $ do     i <- i'     pure-      ( View $ const $ toView i a+      ( View $ const $ toView i       , pure $         Ok           ( Proved@@ -173,7 +217,7 @@  -- | used for groups of checkboxes, @\<select multiple=\"multiple\"\>@ boxes inputMulti-  :: forall m input err view a lbl. (Functor m, FormError err, ErrorInputType err ~ input, FormInput input, Monad m)+  :: forall m input err view a lbl. (FormError err, ErrorInputType err ~ input, FormInput input, Monad m)   => FormState m input FormId   -> [(a, lbl)] -- ^ value, label, initially checked   -> (FormId -> [(FormId, Int, lbl, Bool)] -> view) -- ^ function which generates the view@@ -230,7 +274,7 @@  -- | radio buttons, single @\<select\>@ boxes inputChoice-  :: forall a m err input lbl view. (Functor m, FormError err, ErrorInputType err ~ input, FormInput input, Monad m)+  :: forall a m err input lbl view. (FormError err, ErrorInputType err ~ input, FormInput input, Monad m)   => FormState m input FormId   -> (a -> Bool) -- ^ is default   -> [(a, lbl)] -- ^ value, label@@ -305,7 +349,7 @@  -- | radio buttons, single @\<select\>@ boxes inputChoiceForms-  :: forall a m err input lbl view. (Functor m, Monad m, FormError err, ErrorInputType err ~ input, FormInput input)+  :: forall a m err input lbl view. (Monad m, FormError err, ErrorInputType err ~ input, FormInput input)   => FormState m input FormId   -> a   -> [(Form m input err view a, lbl)] -- ^ value, label@@ -480,10 +524,11 @@   (View v, r) <- unForm form   range <- getFormRange   pure-    ( View -        (\x -> +    ( View+        (\x ->           let errs = retainChildErrors range x           in f (v x) errs         )     , r     )+
src/Ditto/Generalized/Named.hs view
@@ -15,13 +15,24 @@ input :: (Monad m, FormError err) => String -> (input -> Either err a) -> (FormId -> a -> view) -> a -> Form m input err view a input name = G.input (getNamedFormId name) ++-- | used to construct elements with optional initial values, which are still required+inputMaybeReq+  :: (Monad m, FormError err)+  => String+  -> (input -> Either err a)+  -> (FormId -> Maybe a -> view)+  -> Maybe a+  -> Form m input err view a+inputMaybeReq name = G.inputMaybeReq (getNamedFormId name)+ -- | used for elements like @\<input type=\"submit\"\>@ which are not always present in the form submission data. inputMaybe   :: (Monad m, FormError err)   => String   -> (input -> Either err a)-  -> (FormId -> a -> view)-  -> a+  -> (FormId -> Maybe a -> view)+  -> Maybe a   -> Form m input err view (Maybe a) inputMaybe name = G.inputMaybe (getNamedFormId name) @@ -29,8 +40,7 @@ inputNoData   :: (Monad m)   => String-  -> (FormId -> a -> view)-  -> a+  -> (FormId -> view)   -> Form m input err view () inputNoData name = G.inputNoData (getNamedFormId name) @@ -44,7 +54,7 @@  -- | used for groups of checkboxes, @\<select multiple=\"multiple\"\>@ boxes inputMulti-  :: forall m input err view a lbl. (Functor m, FormError err, ErrorInputType err ~ input, FormInput input, Monad m)+  :: forall m input err view a lbl. (FormError err, ErrorInputType err ~ input, FormInput input, Monad m)   => String   -> [(a, lbl)] -- ^ value, label, initially checked   -> (FormId -> [(FormId, Int, lbl, Bool)] -> view) -- ^ function which generates the view@@ -54,7 +64,7 @@  -- | radio buttons, single @\<select\>@ boxes inputChoice-  :: forall a m err input lbl view. (Functor m, FormError err, ErrorInputType err ~ input, FormInput input, Monad m)+  :: forall a m err input lbl view. (FormError err, ErrorInputType err ~ input, FormInput input, Monad m)   => String   -> (a -> Bool) -- ^ is default   -> [(a, lbl)] -- ^ value, label@@ -64,7 +74,7 @@  -- | radio buttons, single @\<select\>@ boxes inputChoiceForms-  :: forall a m err input lbl view. (Functor m, Monad m, FormError err, ErrorInputType err ~ input, FormInput input)+  :: forall a m err input lbl view. (Monad m, FormError err, ErrorInputType err ~ input, FormInput input)   => String   -> a   -> [(Form m input err view a, lbl)] -- ^ value, label
src/Ditto/Proof.hs view
@@ -10,9 +10,9 @@ module Ditto.Proof where  import Control.Monad.Trans (lift)-import Numeric (readDec, readFloat, readSigned) import Ditto.Core (Form (..), Proved (..)) import Ditto.Result (Result (..))+import Numeric (readDec, readFloat, readSigned)  -- | A 'Proof' attempts to prove something about a value. --
src/Ditto/Result.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE DeriveFunctor #-}+ -- | Module for the core result type, and related functions -- module Ditto.Result@@ -22,25 +24,18 @@ import qualified Data.List.NonEmpty as NE  -- | Type for failing computations--- data Result e ok   = Error [(FormRange, e)]   | Ok ok-  deriving (Show, Eq)--instance Functor (Result e) where-  fmap _ (Error x) = Error x-  fmap f (Ok x) = Ok (f x)+  deriving (Show, Eq, Functor)  instance Monad (Result e) where   return = Ok-   Error x >>= _ = Error x   Ok x >>= f = f x  instance Applicative (Result e) where   pure = Ok-   Error x <*> Error y = Error $ x ++ y   Error x <*> Ok _ = Error x   Ok _ <*> Error y = Error y@@ -52,24 +47,18 @@ getResult (Ok r) = Just r  -- | An ID used to identify forms--- data FormId   = FormId-      { -- | Global prefix for the form-        formPrefix :: String-      , -- | Stack indicating field. Head is most specific to this item-        formIdList :: NonEmpty Int-      }-  | FormIdCustom String Int+      String -- ^ Global prefix for the form+      (NonEmpty Int) -- ^ Stack indicating field. Head is most specific to this item+  | FormIdCustom +      String -- ^ Local name of the input+      Int -- ^ Index of the input   deriving (Eq, Ord)  -- | The zero ID, i.e. the first ID that is usable--- zeroId :: String -> FormId-zeroId p = FormId-  { formPrefix = p-  , formIdList = pure 0-  }+zeroId prefix = FormId prefix (pure 0)  -- | map a function over the @NonEmpty Int@ inside a 'FormId' mapId :: (NonEmpty Int -> NonEmpty Int) -> FormId -> FormId@@ -78,22 +67,23 @@  instance Show FormId where   show (FormId p xs) =-    p ++ "-fval-" ++ (intercalate "." $ reverse $ map show $ NE.toList xs)+    p ++ "-fval-" ++ (intercalate "." $ reverseMap show $ NE.toList xs)   show (FormIdCustom x _) = x +reverseMap :: Foldable t => (a -> b) -> t a -> [b]+reverseMap f = foldl (\as a -> f a : as ) []+ -- | get the head 'Int' from a 'FormId' formId :: FormId -> Int formId (FormId _ (x :| _)) = x formId (FormIdCustom _ x) = x  -- | A range of ID's to specify a group of forms--- data FormRange   = FormRange FormId FormId   deriving (Eq, Show)  -- | Increment a form ID--- incrementFormId :: FormId -> FormId incrementFormId (FormId p (x :| xs)) = FormId p $ (x + 1) :| xs incrementFormId x@FormIdCustom{} = x@@ -103,7 +93,6 @@ unitRange i = FormRange i $ incrementFormId i  -- | Check if a 'FormId' is contained in a 'FormRange'--- isInRange   :: FormId -- ^ Id to check for   -> FormRange -- ^ Range@@ -111,7 +100,6 @@ isInRange a (FormRange b c) = formId a >= formId b && formId a < formId c  -- | Check if a 'FormRange' is contained in another 'FormRange'--- isSubRange   :: FormRange -- ^ Sub-range   -> FormRange -- ^ Larger range@@ -122,12 +110,10 @@     formId d  -- | Select the errors for a certain range--- retainErrors :: FormRange -> [(FormRange, e)] -> [e] retainErrors range = map snd . filter ((== range) . fst)  -- | Select the errors originating from this form or from any of the children of -- this form--- retainChildErrors :: FormRange -> [(FormRange, e)] -> [e] retainChildErrors range = map snd . filter ((`isSubRange` range) . fst)