packages feed

verdict (empty) → 0.0.0.0

raw patch · 12 files changed

+734/−0 lines, 12 filesdep +basedep +hspecdep +markdown-unlitsetup-changed

Dependencies added: base, hspec, markdown-unlit, mtl, text, transformers, verdict

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2015, Julian K. Arni++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Julian K. Arni nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ examples/Tutorial.lhs view
@@ -0,0 +1,148 @@+# Verdict++*Very experimental!*++~~~ {.haskell}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE OverloadedStrings #-}+import GHC.TypeLits+import Data.Proxy+import Data.Monoid+import qualified Data.Text as Text+import Verdict+~~~++## A very general smart constructor++Sometimes you want to provide extra guarantees about a type that are best+expressed in terms of a smart constructor with an opaque type:+++~~~ {.haskell}+newtype NonEmptyList' a = NonEmptyList' { getList :: [a] }+makeNonEmptyList :: [a] -> Maybe (NonEmptyList' a)+makeNonEmptyList x | null x    = Nothing+                   | otherwise = Just (NonEmptyList' x)+~~~++Where the `NonEmptyList` constructor is not exported.++This works, but it has quite a few downsides. You can no longer derive things+like `Read` and `FromJSON` without losing the invariants you set up. If you+have a `NonEmptyList` that is also of maximum length 100, you need to either+have a newtype to again wrap your `NonEmptyList`, or a newtype that expresses+both those constraints. Either way, all the functions you wrote for+`NonEmptyList`, and which ideally would work with `NonEmptyMax100List`, won't+do so (without some newtype unwrapping or coercing).++Instead, with `verdict` you can do this:++~~~ {.haskell}+type NonEmptyList a = Validated (Not (Length 0)) [a]+~~~++The smart constructor comes for free as `validate`. Specialized for clarity:++~~~ {.haskell}+mkNonEmpty :: [a] -> Either ErrorTree (NonEmptyList a)+mkNonEmpty = validate+~~~++The `Read` instance also comes for free, and checks for validity:++~~~ {.haskell}+testRead :: NonEmptyList Int+testRead = read "[]"+~~~++`testRead` will throw a descriptive error message.++## With some of the power of refinement types++Writing functions that express their precise assumptions can then be done with+`Implies`:++~~~ {.haskell}+safeHead :: (c `Implies` (Not (Length 0))) => Validated c [a] -> a+safeHead = head . getVal+~~~++Now, if we have another type:++~~~ {.haskell}+type ReasonableList a = Validated (MinLength 1 :&& MaxLength 100) [a]+~~~++Our function `safeHead` will typecheck when given either a `ReasonableList` or+a `NonEmptyList` (or anything else with constraints that imply the length is+not zero).++The library provides some terms that can be used in the little constraint DSL.+Additionally, you may want to create your own. Doing so is as easy as declaring+an empty datatype, and a `HaskVerdict` instance for it. Let us for example+define a predicate `InCircle cx cy r`, which is true of a `Point` if the point+is inside a circle of radius `r` centered on coordinates `(cx, cy)`:++~~~ {.haskell}+data InCircle centerX centerY radius+data Point = Point { x :: Integer, y :: Integer } deriving (Eq, Show)++instance (KnownNat cx, KnownNat cy, KnownNat r)+  => HaskVerdict (InCircle cx cy r) Point where+   haskVerdict _+      = check (\p -> (x p - centerX) ^ 2 + (y p - centerY) ^ 2 < radius ^ 2) err+      where centerX = natVal (Proxy :: Proxy cx)+            centerY = natVal (Proxy :: Proxy cy)+            radius = natVal (Proxy :: Proxy r)+            err = "Point not inside circle: " <> (Text.pack $ show ((centerX, centerY), radius))+~~~++Additionally, you may also want to declare type instances for `Implies'` which+describe what things imply or are implied by your new constraint.++~~~ {.haskell}+type instance Implies' (InCircle cx cy r) (InCircle cx cy r') = r <= r'+~~~++## And opt-in validation capabilities++Sometimes you may not want to or be able to change the types of records. You+can still validate them:++~~~ {.haskell}+mkUpperRightQuad :: ApplicativeError ErrorTree m+                 => Integer -> Integer -> m Point+mkUpperRightQuad x' y' = Point <$> x' `checkWith` (Proxy :: Proxy (Minimum 0))+                               <*> y' `checkWith` (Proxy :: Proxy (Minimum 0))+~~~++If we instantiate `ApplicativeError` to a short-circuiting applicative (like+`Either`), only the first result will be returned. If we use something like+`Failure`, all of them will:++~~~ {.haskell}+main :: IO ()+main = do+  print (mkUpperRightQuad 5 3 :: Either ErrorTree Point)+  print (mkUpperRightQuad (-2) 1 :: Either ErrorTree Point)+  print (mkUpperRightQuad (-2) (- 1) :: Either ErrorTree Point)+  print (mkUpperRightQuad (-2) (- 1) :: Failure ErrorTree Point)+  print (mkUpperRightQuad 0 (- 1) :: Either ErrorTree Point)+  print testRead+~~~++The results:+++    Right (Point {x = 5, y = 3})+    Left (Leaf "Should be more than 0")+    Left (Leaf "Should be more than 0")+    Failure (And (Leaf "Should be more than 0") (Leaf "Should be more than 0"))+    Left (Leaf "Should be more than 0")+    tutorial: Leaf "this still needs to be figured out"
+ src/Verdict.hs view
@@ -0,0 +1,40 @@+module Verdict+    (++    -- * Essentials+      Validated+    , validate+    , getVal+    , unsafeCoerceVal+    , check+    , checkWith+    , HaskVerdict(..)+    , Implies+    , Implies'+    , KnownVal(..)+    , (|.)++    -- * Verdict Terms+    , (:&&)+    , (:||)+    , Not+    , Minimum+    , Maximum+    , MaxLength+    , MinLength+    , Length+    , MultipleOf+    , HasElem++    -- * Errors+    , ErrorTree+    , ErrorTree'(..)+    , Failure(..)+    , ApplicativeError(..)+    ) where++import           Verdict.Class+import           Verdict.Failure+import           Verdict.Logic+import           Verdict.Types+import           Verdict.Val
+ src/Verdict/Class.hs view
@@ -0,0 +1,103 @@+{-# LANGUAGE OverloadedStrings #-}+module Verdict.Class where++import           Control.Monad+import           Data.Monoid+import           Data.Proxy+import qualified Data.Text     as Text+import           GHC.TypeLits+import           Verdict.Types++------------------------------------------------------------------------------+-- * HaskVerdict+------------------------------------------------------------------------------+class HaskVerdict a b where+    haskVerdict :: Proxy a -> b -> Maybe ErrorTree+++------------------------------------------------------------------------------+-- * Logical Base Terms+------------------------------------------------------------------------------+instance (HaskVerdict a r, HaskVerdict b r) => HaskVerdict (a :&& b) r where+    haskVerdict _ x = And <$> haskVerdict pa x <*> haskVerdict pb x+      where pa = Proxy :: Proxy a+            pb = Proxy :: Proxy b++instance (HaskVerdict a r, HaskVerdict b r) => HaskVerdict (a :|| b) r where+    haskVerdict _ x = Or <$> haskVerdict pa x <*> haskVerdict pb x+      where pa = Proxy :: Proxy a+            pb = Proxy :: Proxy b++instance (HaskVerdict c a) => HaskVerdict (Not c) a where+    haskVerdict _ x = case haskVerdict p x of+        Just _ -> Nothing+        Nothing -> Just (Leaf "this still needs to be figured out")+      where p = Proxy :: Proxy c++instance HaskVerdict 'True a where+    haskVerdict _ _ = Nothing++------------------------------------------------------------------------------+-- * Datatype Representation Terms+------------------------------------------------------------------------------++{-instance ( G.Generic a ) => HaskVerdict (a :* b) r where-}+    {-haskVerdict _ x = foldM (G.hcollapse . G.hap r $ G.from x)-}+      {-where pa = Proxy :: Proxy a-}+            {-pb = Proxy :: Proxy b-}+            {-v1 = G.Fn $ \(G.I n) -> G.K (haskVerdict pa n)-}+            {-v2 = G.Fn $ \(G.I n) -> G.K (haskVerdict pb n)-}+            {-r  = (v1 G.:* v2 G.:* G.Nil) G.:* G.Nil-}++------------------------------------------------------------------------------+-- * Other Base Terms+------------------------------------------------------------------------------+instance HaskVerdict () a where+    haskVerdict _ = const Nothing++instance (Ord b, Show b, KnownVal a b) => HaskVerdict (Maximum a) b where+    haskVerdict _ = check (<= p) ("Should be less than " <> showT p)+      where p = knownVal (Proxy :: Proxy a)++instance (Ord b, Show b, KnownVal a b) => HaskVerdict (Minimum a) b where+    haskVerdict _ = check (>= p) ("Should be more than " <> showT p)+      where p = knownVal (Proxy :: Proxy a)++instance (Foldable f, Show (f b), KnownNat a)+       => HaskVerdict (MaxLength a) (f b) where+    haskVerdict _ = check ((<= p) . length)+                          ("Should be of length less than " <> showT p)+      where p = fromInteger $ natVal (Proxy :: Proxy a)++instance (Foldable f, Show (f b), KnownNat a)+       => HaskVerdict (MinLength a) (f b) where+    haskVerdict _ = check ((>= p) . length)+                          ("Should be of length more than " <> showT p)+      where p = fromInteger $ knownVal (Proxy :: Proxy a)++instance (Foldable t, KnownNat a) => HaskVerdict (Length a) (t b) where+    haskVerdict _ = check ((== p) . length) ("Should be of length " <> showT p)+      where p = fromInteger $ natVal (Proxy :: Proxy a)++instance (Foldable t, Show b, Eq b, KnownVal a b)+    => HaskVerdict (HasElem a) (t b) where+    haskVerdict _ = check (elem p) ("Should contain " <> showT p)+      where p = knownVal (Proxy :: Proxy a)++instance (KnownNat n, Integral a) => HaskVerdict (MultipleOf n) a where+    haskVerdict _ = check (\x -> (toInteger x `rem` p) == 0) ("Not a multiple of " <> showT p)+      where p = natVal (Proxy :: Proxy n)++showT :: Show a => a -> Text.Text+showT = Text.pack . show++check :: (x -> Bool) -> err -> x -> Maybe (ErrorTree' err)+check pred' err x = guard (not $ pred' x) >> pure (Leaf err)++------------------------------------------------------------------------------+-- Known Val+class KnownVal a b | a -> b where+    knownVal :: Proxy a -> b++instance KnownNat n => KnownVal n Integer where+    knownVal = natVal
+ src/Verdict/Failure.hs view
@@ -0,0 +1,42 @@+module Verdict.Failure+    ( Failure(..)+    , ApplicativeError(..)+    ) where++import           Control.Exception          (IOException, catch)+import qualified Control.Monad.Trans.Except as ExceptT+import           Data.Monoid+import           Data.Typeable              (Typeable)+import           GHC.Generics               (Generic)++data Failure e a = Failure e | Success a+  deriving (Eq, Show, Functor, Generic, Typeable)++class Applicative m => ApplicativeError e m | m -> e where+    throwError :: e -> m a+    catchError :: m a -> (e -> m a) -> m a++instance Monoid e => Applicative (Failure e) where+    pure                            = Success+    Failure msgs  <*> Failure msgs' = Failure (msgs <> msgs')+    Success _     <*> Failure msgs' = Failure msgs'+    Failure msgs' <*> Success _     = Failure msgs'+    Success f     <*> Success x     = Success (f x)++instance Monoid e => ApplicativeError e (Failure e) where+    throwError               = Failure+    catchError (Failure e) f = f e+    catchError s           _ = s++instance ApplicativeError e (Either e) where+    throwError            = Left+    catchError (Left e) f = f e+    catchError s        _ = s++instance ApplicativeError IOException IO where+    throwError            = ioError+    catchError            = catch++instance Monad m => ApplicativeError e (ExceptT.ExceptT e m) where+    throwError            = ExceptT.throwE+    catchError            = ExceptT.catchE
+ src/Verdict/Logic.hs view
@@ -0,0 +1,27 @@+{-# LANGUAGE PolyKinds            #-}+{-# LANGUAGE UndecidableInstances #-}+module Verdict.Logic where++import           GHC.Exts      (Constraint)+import           GHC.TypeLits+import           Verdict.Types+++type family Or (a :: Constraint) (b :: Constraint) :: Constraint where+    Or () x = ()+    Or x () = ()++type family Implies (a :: k) (b :: k) :: Constraint where+    Implies (a :&& b) c = (a `Implies` c) `Or` (b `Implies` c)+    Implies a (b :|| c) = (a `Implies` b) `Or` (a `Implies` c)+    Implies a (Not a) = ('True ~ 'False)+    Implies (Not a) a = ('True ~ 'False)+    Implies a (Not (Not a)) = ()+    Implies a 'True = ()+    Implies a b = Implies' a b++type family Implies' a b :: Constraint+type instance Implies' (Length a) (MaxLength b) = a <= b+type instance Implies' (Length a) (MinLength b) = b <= b+type instance Implies' (MaxLength a) (MaxLength b) = a <= b+type instance Implies' (MinLength a) (MinLength b) = b <= a
+ src/Verdict/Types.hs view
@@ -0,0 +1,51 @@+{-# LANGUAGE DeriveFunctor #-}+module Verdict.Types where++import qualified Data.Text as Text++------------------------------------------------------------------------------+-- * Logical Base Terms+------------------------------------------------------------------------------+data a :&& b = a :&& b+infixr 3 :&&+data a :|| b = a :|| b+infixr 2 :||+data Not a++------------------------------------------------------------------------------+-- * Datatype Representation Terms+------------------------------------------------------------------------------++data a :* b+infixr 5 :*++------------------------------------------------------------------------------+-- * Other Base Terms+------------------------------------------------------------------------------++data Minimum a+data Maximum a+data MaxLength a+data MinLength a+data Length a+data MultipleOf a++data HasElem a++------------------------------------------------------------------------------+-- * Other Types+------------------------------------------------------------------------------++-- | Error representation+data ErrorTree' e+  = Leaf e+  | Or  (ErrorTree' e) (ErrorTree' e)+  | And (ErrorTree' e) (ErrorTree' e)+  | Sum (ErrorTree' e) (ErrorTree' e)+  deriving (Eq, Show, Functor)++type ErrorTree = ErrorTree' Text.Text++instance Monoid e => Monoid (ErrorTree' e) where+    mempty  = Leaf mempty+    mappend = And
+ src/Verdict/Val.hs view
@@ -0,0 +1,122 @@+{-# LANGUAGE ConstraintKinds     #-}+{-# LANGUAGE ScopedTypeVariables #-}+module Verdict.Val where++import           Control.Monad.Fix+import           Control.Monad.Zip+import           Data.Coerce     (Coercible, coerce)+import           Data.Foldable+import           Data.Proxy+import           Data.String     (IsString (..))+import           Text.Read++import           Verdict.Class+import           Verdict.Failure+import           Verdict.Logic+import           Verdict.Types++------------------------------------------------------------------------------+-- * Validated+------------------------------------------------------------------------------+-- | A generalization of smart constructors with opaque types.+-- Construct a @Validated@ with 'validate'.+newtype Validated constraint a = Validated { getVal :: a }+    deriving (Show, Eq, Ord)++-- * Validated ()++-- @Validated ()@ is the same as 'Data.Functor.Identity'; we use the same+-- instances.+validateEmpty :: a -> Validated () a+validateEmpty = coerce++instance Functor (Validated ()) where+    fmap = coerce++instance Applicative (Validated ()) where+    pure  = Validated+    (<*>) = coerce++instance Monad (Validated ()) where+    return = Validated+    m >>= k = k (getVal m)++instance MonadFix (Validated ()) where+    mfix f = Validated (fix (getVal . f))++instance MonadZip (Validated ()) where+    mzipWith = coerce+    munzip   = coerce++instance Monoid m => Monoid (Validated () m) where+    mempty       = Validated mempty+    mappend a b  = Validated $ mappend (getVal a) (getVal b)++instance Foldable (Validated ()) where+    foldMap                 = coerce+    elem                    = (. getVal) #. (==)+    foldl                   = coerce+    foldl'                  = coerce+    foldl1 _                = getVal+    foldr f z (Validated x) = f x z+    foldr'                  = foldr+    foldr1 _                = getVal+    length _                = 1+    maximum                 = getVal+    minimum                 = getVal+    null _                  = False+    product                 = getVal+    sum                     = getVal+    toList (Validated x)    = [x]++instance (HaskVerdict c v, Read v) => Read (Validated c v) where+    readPrec = force . validate <$> readPrec+      where force = either (error . show) id++instance (HaskVerdict c v, IsString v) => IsString (Validated c v) where+    fromString = force . validate . fromString+      where force = either (error . show) id+++-- | Constructs a @Validated c a@ from an @a@ if @a@ matches the constraints;+-- throws an error with a description of precise constraints not satisfied+-- otherwise.+validate :: forall c a m . (HaskVerdict c a, ApplicativeError ErrorTree m)+    => a -> m (Validated c a)+validate a = case haskVerdict (Proxy :: Proxy c) a of+    Nothing -> pure $ Validated a+    Just err -> throwError err++-- | Coerce a 'Validated' to another set of constraints. This is safe with+-- respect to memory corruption, but loses the guarantee that the values+-- satisfy the predicates.+unsafeCoerceVal :: Validated c a -> Validated c' a+unsafeCoerceVal = coerce++protect :: ( ApplicativeError (String, ErrorTree) m+           , HaskVerdict c a+           ) => Proxy c -> String -> (a -> b) -> a -> m b+protect p name fn a = case haskVerdict p a of+    Nothing -> pure $ fn a+    Just e  -> throwError (name, e)++-- | Checks a non-'Validated' value against a set of constraints given by a+-- 'Proxy'.+checkWith :: forall m c a . (ApplicativeError ErrorTree m, HaskVerdict c a)+          => a -> Proxy c -> m a+checkWith v _ = getVal <$> v'+  where v' = validate v :: ApplicativeError ErrorTree m => m (Validated c a)++-- | Function composition. Typechecks if the result of applying the first+-- function has a constraint that implies the constraint of the argument of the+-- second function.+(|.) :: (cb' `Implies` cb)+    => (Validated cb b -> Validated cc c)+    -> (Validated ca a -> Validated cb' b)+    -> Validated ca a -> Validated cc c+f |. g = f . coerce . g+infixr 8 |.++-- * Internal+(#.) :: Coercible b c => (b -> c) -> (a -> b) -> (a -> c)+(#.) _f = coerce
+ test/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
+ test/VerdictSpec.hs view
@@ -0,0 +1,96 @@+module VerdictSpec (spec) where++import Data.Either+import Test.Hspec+import Verdict++spec :: Spec+spec = describe "Verdict" $ do+    maximumSpec+    minimumSpec+    maxLengthSpec+    minLengthSpec+    lengthSpec+    multipleOfSpec++maximumSpec :: Spec+maximumSpec = describe "Maximum" $ do++  it "rejects larger values" $ do+    (validate 5 :: Either ErrorTree (Validated (Maximum 3) Integer))+        `shouldSatisfy` isLeft++  it "accepts smaller values" $ do+    (validate 3 :: Either ErrorTree (Validated (Maximum 5) Integer))+        `shouldSatisfy` isRight++  it "accepts equal sized values" $ do+    (validate 3 :: Either ErrorTree (Validated (Maximum 3) Integer))+        `shouldSatisfy` isRight++minimumSpec :: Spec+minimumSpec = describe "Minimum" $ do++  it "rejects smaller values" $ do+    (validate 3 :: Either ErrorTree (Validated (Minimum 5) Integer))+        `shouldSatisfy` isLeft++  it "accepts larger values" $ do+    (validate 5 :: Either ErrorTree (Validated (Minimum 3) Integer))+        `shouldSatisfy` isRight++  it "accepts equal sized values" $ do+    (validate 3 :: Either ErrorTree (Validated (Minimum 3) Integer))+        `shouldSatisfy` isRight++maxLengthSpec :: Spec+maxLengthSpec = describe "MaxLength" $ do++  it "rejects longer values" $ do+    (validate [(),()] :: Either ErrorTree (Validated (MaxLength 1) [()]))+        `shouldSatisfy` isLeft++  it "accepts shorter values" $ do+    (validate [()] :: Either ErrorTree (Validated (MaxLength 3) [()]))+        `shouldSatisfy` isRight++  it "accepts equal sized values" $ do+    (validate [()] :: Either ErrorTree (Validated (MaxLength 1) [()]))+        `shouldSatisfy` isRight++minLengthSpec :: Spec+minLengthSpec = describe "MinLength" $ do++  it "rejects shorter values" $ do+    (validate [()] :: Either ErrorTree (Validated (MinLength 2) [()]))+        `shouldSatisfy` isLeft++  it "accepts shorter values" $ do+    (validate [(), ()] :: Either ErrorTree (Validated (MinLength 1) [()]))+        `shouldSatisfy` isRight++  it "accepts equal sized values" $ do+    (validate [()] :: Either ErrorTree (Validated (MinLength 1) [()]))+        `shouldSatisfy` isRight++lengthSpec :: Spec+lengthSpec = describe "Length" $ do++  it "rejects differing lengths" $ do+    (validate [()] :: Either ErrorTree (Validated (Length 2) [()]))+        `shouldSatisfy` isLeft++  it "accepts matching lengths" $ do+    (validate [()] :: Either ErrorTree (Validated (Length 1) [()]))+        `shouldSatisfy` isRight++multipleOfSpec :: Spec+multipleOfSpec = describe "MultipleOf" $ do++  it "rejects non-multiples" $ do+    (validate 5 :: Either ErrorTree (Validated (MultipleOf 2) Integer))+        `shouldSatisfy` isLeft++  it "accepts multiples" $ do+    (validate 4 :: Either ErrorTree (Validated (MultipleOf 2) Integer))+        `shouldSatisfy` isRight
+ verdict.cabal view
@@ -0,0 +1,72 @@+name:                verdict+version:             0.0.0.0+synopsis:            Validation framework+description:+  DO NOT USE! Unstable, not thoroughly tested.+license:             BSD3+license-file:        LICENSE+author:              Julian K. Arni+maintainer:          jkarni@gmail.com+copyright:           (c) Julian K. Arni+build-type:          Simple+cabal-version:       >=1.10++library+  exposed-modules:     Verdict+  other-modules:       Verdict.Class+                     , Verdict.Failure+                     , Verdict.Types+                     , Verdict.Logic+                     , Verdict.Val+  default-extensions:  TypeOperators+                     , MultiParamTypeClasses+                     , DataKinds+                     , FunctionalDependencies+                     , PolyKinds+                     , ScopedTypeVariables+                     , FlexibleInstances+                     , FlexibleContexts+                     , TypeFamilies+                     , DeriveFunctor+                     , DeriveGeneric+                     , DeriveDataTypeable+  build-depends:       base >=4.7 && <4.9+                     , mtl+                     , transformers+                     , text+  hs-source-dirs:      src+  ghc-options:         -Wall+  default-language:    Haskell2010++test-suite spec+  type:                exitcode-stdio-1.0+  ghc-options:         -Wall+                       -fno-warn-unused-binds+                       -fno-warn-type-defaults+  default-language:    Haskell2010+  hs-source-dirs:      test+  main-is:             Spec.hs+  other-modules:       VerdictSpec+  default-extensions:  DeriveFunctor+                     , TypeOperators+                     , MultiParamTypeClasses+                     , DataKinds+                     , FunctionalDependencies+                     , PolyKinds+                     , ScopedTypeVariables+                     , FlexibleInstances+                     , FlexibleContexts+                     , TypeFamilies+  build-depends:       base == 4.*+                     , verdict+                     , hspec == 2.*++executable tutorial+  main-is:             Tutorial.lhs+  ghc-options:         -Wall -pgmL markdown-unlit+  hs-source-dirs:      examples+  build-depends:       base >=4.7 && <4.9+                     , verdict+                     , text+                     , markdown-unlit+  default-language:    Haskell2010